我正在尝试为 Java 实体添加国际化(多语言)支持。在向每个新字段添加翻译时,我愿意接受尽可能少的样板代码的任何选项。我不限于 JPA,也可以使用 hibernate 注释。在最坏的情况下,纯 sql 也适用。可能有一些我还没有找到的现成库。 应该没有必要遵循我下面描述的想法。
理想情况下,我需要数据库看起来像这样:
i18n
+------+--------+------+
| id | locale | text |
+------+--------+------+
| 1 | en | foo |
+------+--------+------+
| 1 | de | bar |
+------+--------+------+
| 2 | en | foo2 |
+------+--------+------+
| 2 | de | bar2 |
+------+--------+------+
parent
+------+------+
| id | text |
+------+------+
| 99 | 1 |
+------+------+
| 100 | 2 |
+------+------+
i18n
是一个应该只包含 3 列的表:id
、locale
和 text
。表 parent
有一列 text
(如果只有一个字段需要 i18n,否则需要更多列)包含来自 i18n.id
的值。我在父类中尝试了以下映射:
@ElementCollection @CollectionTable(name="i18n", joinColumns = @JoinColumn(referencedColumnName="id"))
@MapKeyColumn(name="locale") @Column(name="text")
public Map<String, String> text = newHashMap();
当禁用 DDL 生成并且我自己创建表时它似乎可以工作,但是当启用 DDL 生成时,它会生成一个不必要的列 i18n.parent_id
和一个约束:
ALTER TABLE PUBLIC.I18N ADD CONSTRAINT
PUBLIC.FK_HVGN9UJ4DJOFGLT8L78BYQ75I FOREIGN KEY(PARENT_ID) INDEX
PUBLIC.FK_HVGN9UJ4DJOFGLT8L78BYQ75I_INDEX_2 REFERENCES
PUBLIC.PARENT(ID) NOCHECK
如何去掉这个多余的列?是否可以避免从 i18n
表到 parent
表的引用?此链接使重用 i18n
表变得困难。我要么需要在 i18n
表中保留一些鉴别器值,要么在整个数据库中使用 GUID,因为不同表中的 id 会发生冲突。第一个选项意味着很多样板代码。第二个选项意味着当前项目中有很多工作要做。
我需要一种可重用的方法来将国际化添加到实体中。我的父类将大致像这样。并且会有几个这样的父类具有必须国际化的不同的字段集。
@Entity
public class Parent {
@Id @GeneratedValue
public Long id;
public String title; // must be in internationalized
public String text; // must be in internationalized
public String details; // must be in internationalized
// ... other fields
}
最佳答案
On the database level collection instances are distinguished by the foreign key of the entity that owns the collection. This foreign key is referred to as the collection key column, or columns, of the collection table.
所以我想你想为你的提议禁用 forgien key 生成,简单地说你可以这样做
@Entity
public class Parent {
@Id
@GeneratedValue
public Long id;
@ElementCollection
@CollectionTable(name = "i18n", foreignKey = @ForeignKey(name = "none"), joinColumns = @JoinColumn(name = "id"))
@MapKeyColumn(name = "locale")
@Column(name = "text")
public Map<String, String> text = new HashMap<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Map<String, String> getText() {
return text;
}
public void setText(Map<String, String> text) {
this.text = text;
}
}
然后当你验证生成的DDL时,forgien key并没有应用到I18N表上,但是还是得到了能力
@Test
public void testParent() {
Parent p = new Parent();
HashMap<String, String> text = new HashMap<>();
text.put("en", "foo");
text.put("de", "bar");
p.setText(text);
entityManager.persist(p);
entityManager.flush();
Parent parent = entityManager.find(Parent.class, p.getId());
System.out.println("en: " + parent.getText().get("en"));
System.out.println("de: " + parent.getText().get("de"));
}
about是一个简单的测试(Spring Boot 1.4 release),会在控制台看到输出:
Hibernate:
create table i18n (
id bigint not null,
text varchar(255),
locale varchar(255) not null,
primary key (id, locale)
)
Hibernate:
create table parent (
id bigint generated by default as identity,
primary key (id)
)
......
Hibernate:
insert
into
parent
(id)
values
(null)
Hibernate:
insert
into
i18n
(id, locale, text)
values
(?, ?, ?)
Hibernate:
insert
into
i18n
(id, locale, text)
values
(?, ?, ?)
en: foo
de: bar
这是 H2 数据库中的表:
关于java - 如何国际化 Hibernate 实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39704729/