Локализуемые строки в JPA - PullRequest
2 голосов
/ 11 мая 2011

Чтобы удовлетворить потребность в локализованных динамических (создаваемых пользователем, хранимых в db) данных в моем проекте Java EE 6, я создал общую таблицу локализованных строк, способную хранить любую строку на любом языке.Это позволяет избежать создания ~ 15 дополнительных таблиц, содержащих только имена для материала.То, что я хотел бы знать, это две вещи:

1) Как вы думаете, это хорошая или плохая идея, и почему?2) Знаете ли вы о каком-либо чистом решении проблемы, перечисленной в разделе Минусы?

Мой опыт:

Плюсы : нужна только одна таблица, общее решение, которое легконастроить как в jpa, так и в дб.Дублированный код не существует.

Минусы : большая проблема, которую я обнаружил, заключается в том, что из-за таблицы muiltilingual_string, теперь зная, какие объекты используют ее, каскадное удаление не будет работать из SQL (но этоработает в JPA с orphanRemoval).Это дает возможность «мертвым» строкам оставаться в базе данных, если они работают вне JPA.

Объекты: (AbstractEntity - это сопоставленный суперкласс, содержащий @id и @version)

@Entity
@Table(schema = "COMPETENCE", name = "multilingual_string")
public class MultilingualString extends AbstractEntity{

    @ElementCollection(fetch=FetchType.EAGER)
    @MapKey(name = "language")
    @CollectionTable(schema = "COMPETENCE", name = "multilingual_string_map",
                     joinColumns = @JoinColumn(name = "string_id"))
    private Map<Language, LocalizedString> map = new HashMap<Language, LocalizedString>();

    public MultilingualString() {}

    public MultilingualString(Language lang, String text) {
        addText(lang, text);
    }

    public void addText(Language lang, String text) {
        map.put(lang, new LocalizedString(lang, text));
    }

    public String getText(Language lang) {
        if (map.containsKey(lang)) {
            return map.get(lang).getText();
        }
        return null;
    }

    public LocalizedString getLocalizedString(Language lang){
        if(map.get(lang) == null)
            map.put(lang, new LocalizedString(lang, null));
        return map.get(lang);
    }

    @Override
    public MultilingualString clone(){
        MultilingualString ms = new MultilingualString();
        for(LocalizedString s : map.values())
            ms.addText(s.getLanguage(), s.getText());
        return ms;
    }

    @Override
    public String toString() {
        return getId() == null ? "null " + this.getClass().getName() : getId().toString();
    }
}

@Embeddable
public class LocalizedString {

    @JoinColumn(name="lang_id")
    private Language language;

    @Column(name="text")
    private String text;

    public LocalizedString() {
    }

    public LocalizedString(Language language, String text) {
        this.language = language;
        this.text = text;
    }

    public Language getLanguage() {
        return language;
    }

    public void setLanguage(Language language) {
        this.language = language;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

}

Классы используются следующим образом в других сущностях:

@OneToOne(cascade=CascadeType.ALL, orphanRemoval=true)
@JoinColumn(name = "summary_stringid")
private MultilingualString summary;

Таблицы:

MULTILINGUAL_STRING (
    id bigint primary key
);

MULTILINGUAL_STRING_MAP (
    string_id bigint FOREIGN KEY REFERENCES MULTILINGUAL_STRING(id), 
    lang_id bigint FOREIGN KEY REFERENCES LANG(id),
    text varchar(2000 char),
    PRIMARY KEY(string_id, lang_id)
);

Используются следующим образом:

COMPETENCE (
    id bigint PRIMARY KEY,
    name_id bigint FOREIGN KEY REFERENCES MULTILINGUAL_STRING(id)
);

Ответы [ 2 ]

1 голос
/ 30 марта 2012

Такой подход имеет смысл, и он не нов. Он был опубликован, обсужден и протестирован с различными провайдерами JPA здесь: http://hwellmann.blogspot.com/2010/07/jpa-20-mapping-map.html (Я полагаю, вы взяли код оттуда)

1 голос
/ 11 мая 2011

Один вопрос, который я вижу, заключается в том, что это, похоже, не работает как каскад ResourceBundle.

В ResourceBundle, если в качестве локали используется ES_es, он сначала будет искать локализованную версию в ES_es, если он не найдет, попытается в ES, а если нет, то будет искать строку по умолчанию (в случае, если онав противном случае будет показана строка #STRING_ID).Вы можете иметь словарь .EN, .EN_uk для выражений, специфичных для Соединенного Королевства, и .EN_us для выражений, специфичных для США, а в EN_uk и EN_us вводить только необходимые ключи.

В вашей системе, он просто смотрит на текущую локаль, не допуская всех этих опций, поэтому вам нужно переопределить все значения для каждой локали.Таким образом, в приведенном выше примере вам нужно будет ввести значение для каждого ключа как для EN_uk, так и для EN_us.

...