2

为了解决我的 Java EE 6 项目中对本地化动态(用户创建,存储在 db 中)数据的需求,我制作了一个通用的本地化字符串表,它能够以任何语言存储任何字符串。这是为了避免不得不制作大约 15 个额外的表,其中只包含东西的名称。我想知道的是两件事:

1)你认为这是一个主意还是主意,为什么?2)您知道针对缺点下列出的问题的任何干净解决方案吗?

我的经历:

优点:只需要一张表,在 jpa 和 db 中都易于配置的通用解决方案。不存在重复的代码。

缺点:我发现的一个大问题是,由于 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)
);
4

2 回答 2

1

我看到的一个问题是,这似乎不像 ResourceBundle 的级联那样工作。

在 ResourceBundle 中,如果你的 locale 是 ES_es,它会首先在 ES_es 中寻找本地化版本,如果没有找到会在 ES 中尝试,如果没有,它会寻找默认的 String(如果失败会显示 # STRING_ID 字符串)。你可以有一个 .EN 字典,一个 .EN_uk 表示英国特有的表达式,一个 .EN_us 表示美国特有的表达式,并且在 EN_uk 和 EN_us 中只放置所需的键。

对于您的系统,它只查看当前语言环境而不允许所有这些选项,因此您必须重新定义每个语言环境的所有值。因此,在上面的示例中,您必须为 EN_uk 和 EN_us 的每个键输入一个值。

于 2011-05-11T12:29:18.857 回答
1

这种方法确实有意义,而且并不新鲜。它在此处针对不同的 JPA 提供程序发布、讨论和测试:http: //hwellmann.blogspot.com/2010/07/jpa-20-mapping-map.html (我相信您从那里获取了代码)

于 2012-03-30T19:18:42.347 回答