在 JPA 中建模隐式多对多关系
Modeling implicit many to many relations in JPA
考虑我想在 JPA 中表示的以下遗留数据模型:
- 语言有
id
和 name
。
- 语言描述有一个复合主键,由一个
id
和一个 language_id
以及一个 description
组成,因此对于每种翻译,每种语言都有一个条目。
- 文章有(国际化)描述,由上述
languagedescription_id
标识。
现在,我的第一个方法是对实体建模,例如
@Entity
public class Language {
@Id
@Column(length = 3)
private String id;
private String languageDescription;
// ...
}
@Embeddable
public class LanguageDescriptionId implements Serializable {
@Column(length = 9)
private String id;
@Column(length = 3)
private String languageId;
// ...
}
@Entity
public class LanguageDescription {
@EmbeddedId
private LanguageDescriptionId languageDescriptionId;
@ManyToOne
@MapsId(value = "languageId")
private Language language;
@Column(length = 60)
private String description;
// ...
}
@Entity
public class Article {
@Id
private String id;
@ManyToOne
private LanguageDescription languageDescription;
}
其中 a) 是不正确的,因为从技术上讲,文章可以有一个翻译列表(理想情况下是语言到语言描述的映射,以便轻松获取给定语言的翻译)和 b) 导致数据库情况,我的 ORM (EclipseLink) 将 languagedescription
的两个主键列添加到 article
table,尽管文章仅引用 languagedescription_id
以防止出现多个冗余条目。将 Article
实体替换为
时
@Entity
public class Article {
@Id
private String id;
@ManyToMany
private List<LanguageDescription> languageDescriptions;
}
我的 ORM 创建了一个映射 table,我必须手动填充它,这将使用冗余信息扩展(遗留)数据模型。目前我的方法是对 Article
实体建模,例如
@Entity
public class Article {
@Id
private String id;
@Column(length = 9)
private String languageDescriptionId;
}
同时省略 JPA 支持并在 @EntityListener
中获取翻译。
现在,这种情况是否可以在 JPA 中以某种方式自动管理,或者 JPA 根本不支持这种 "implicit" 没有映射的多对多关系 table?
例子
public class Main {
public static void main(String[] args) {
final Language english = createLanguage("language_1", "English");
final Language german = createLanguage("language_2", "German");
createArticle1(english, german);
createArticle2(english, german);
}
private static Language createLanguage(String id, String languageDescription) {
final Language language = new Language();
language.setId(id);
language.setLanguageDescription(languageDescription);
return language;
}
private static void createArticle1(Language english, Language german) {
final String languageDescriptionId = "languagedescription_1";
final LanguageDescription languageDescription1 = new LanguageDescription();
final LanguageDescriptionId languageDescriptionId1 = new LanguageDescriptionId();
languageDescriptionId1.setId(languageDescriptionId);
languageDescriptionId1.setLanguageId(english.getId());
languageDescription1.setLanguageDescriptionId(languageDescriptionId1);
languageDescription1.setLanguage(english);
languageDescription1.setDescription("Engine");
final LanguageDescription languageDescription2 = new LanguageDescription();
final LanguageDescriptionId languageDescriptionId2 = new LanguageDescriptionId();
languageDescriptionId2.setId(languageDescriptionId);
languageDescriptionId2.setLanguageId(german.getId());
languageDescription2.setLanguageDescriptionId(languageDescriptionId2);
languageDescription2.setLanguage(german);
languageDescription2.setDescription("Motor");
final Article article1 = new Article();
article1.setId("a_1");
article1.setLanguageDescriptionId(languageDescriptionId);
}
private static void createArticle2(Language english, Language german) {
final String languageDescriptionId = "languagedescription_2";
final LanguageDescription languageDescription3 = new LanguageDescription();
final LanguageDescriptionId languageDescriptionId3 = new LanguageDescriptionId();
languageDescriptionId3.setId(languageDescriptionId);
languageDescriptionId3.setLanguageId(english.getId());
languageDescription3.setLanguageDescriptionId(languageDescriptionId3);
languageDescription3.setLanguage(english);
languageDescription3.setDescription("Turn Signal");
final LanguageDescription languageDescription4 = new LanguageDescription();
final LanguageDescriptionId languageDescriptionId4 = new LanguageDescriptionId();
languageDescriptionId4.setId(languageDescriptionId);
languageDescriptionId4.setLanguageId(german.getId());
languageDescription4.setLanguageDescriptionId(languageDescriptionId4);
languageDescription4.setLanguage(german);
languageDescription4.setDescription("Blinker");
final Article article2 = new Article();
article2.setId("a_2");
article2.setLanguageDescriptionId(languageDescriptionId);
}
}
可能会出现如下情况:
@Entity
public class Article {
@Id
private String id;
@Column(length = 9)
private String languageDescriptionId;
@OneToMany
@JoinColumn(name="ID", referencedColumnName="LANGUAGEDESCRIPTIONID", insertable=false, updatable=false)
private List<LanguageDescription> languageDescriptions;
}
现在上面的 OneToMany 不符合 JPA,因为 LanguageDescription 有一个复合 PK,所以 EclipseLink 和其他提供程序将抛出验证异常,这只是它看起来的一个例子。您将需要使其成为瞬态或添加另一个连接列,只是为了绕过验证,然后在描述符定制器中修改或添加映射。这在此处 jpa, eclips-link 2.5.1: OneToMany not working on columns not primary key
的答案中有所描述和显示
考虑我想在 JPA 中表示的以下遗留数据模型:
- 语言有
id
和name
。 - 语言描述有一个复合主键,由一个
id
和一个language_id
以及一个description
组成,因此对于每种翻译,每种语言都有一个条目。 - 文章有(国际化)描述,由上述
languagedescription_id
标识。
现在,我的第一个方法是对实体建模,例如
@Entity
public class Language {
@Id
@Column(length = 3)
private String id;
private String languageDescription;
// ...
}
@Embeddable
public class LanguageDescriptionId implements Serializable {
@Column(length = 9)
private String id;
@Column(length = 3)
private String languageId;
// ...
}
@Entity
public class LanguageDescription {
@EmbeddedId
private LanguageDescriptionId languageDescriptionId;
@ManyToOne
@MapsId(value = "languageId")
private Language language;
@Column(length = 60)
private String description;
// ...
}
@Entity
public class Article {
@Id
private String id;
@ManyToOne
private LanguageDescription languageDescription;
}
其中 a) 是不正确的,因为从技术上讲,文章可以有一个翻译列表(理想情况下是语言到语言描述的映射,以便轻松获取给定语言的翻译)和 b) 导致数据库情况,我的 ORM (EclipseLink) 将 languagedescription
的两个主键列添加到 article
table,尽管文章仅引用 languagedescription_id
以防止出现多个冗余条目。将 Article
实体替换为
@Entity
public class Article {
@Id
private String id;
@ManyToMany
private List<LanguageDescription> languageDescriptions;
}
我的 ORM 创建了一个映射 table,我必须手动填充它,这将使用冗余信息扩展(遗留)数据模型。目前我的方法是对 Article
实体建模,例如
@Entity
public class Article {
@Id
private String id;
@Column(length = 9)
private String languageDescriptionId;
}
同时省略 JPA 支持并在 @EntityListener
中获取翻译。
现在,这种情况是否可以在 JPA 中以某种方式自动管理,或者 JPA 根本不支持这种 "implicit" 没有映射的多对多关系 table?
例子
public class Main {
public static void main(String[] args) {
final Language english = createLanguage("language_1", "English");
final Language german = createLanguage("language_2", "German");
createArticle1(english, german);
createArticle2(english, german);
}
private static Language createLanguage(String id, String languageDescription) {
final Language language = new Language();
language.setId(id);
language.setLanguageDescription(languageDescription);
return language;
}
private static void createArticle1(Language english, Language german) {
final String languageDescriptionId = "languagedescription_1";
final LanguageDescription languageDescription1 = new LanguageDescription();
final LanguageDescriptionId languageDescriptionId1 = new LanguageDescriptionId();
languageDescriptionId1.setId(languageDescriptionId);
languageDescriptionId1.setLanguageId(english.getId());
languageDescription1.setLanguageDescriptionId(languageDescriptionId1);
languageDescription1.setLanguage(english);
languageDescription1.setDescription("Engine");
final LanguageDescription languageDescription2 = new LanguageDescription();
final LanguageDescriptionId languageDescriptionId2 = new LanguageDescriptionId();
languageDescriptionId2.setId(languageDescriptionId);
languageDescriptionId2.setLanguageId(german.getId());
languageDescription2.setLanguageDescriptionId(languageDescriptionId2);
languageDescription2.setLanguage(german);
languageDescription2.setDescription("Motor");
final Article article1 = new Article();
article1.setId("a_1");
article1.setLanguageDescriptionId(languageDescriptionId);
}
private static void createArticle2(Language english, Language german) {
final String languageDescriptionId = "languagedescription_2";
final LanguageDescription languageDescription3 = new LanguageDescription();
final LanguageDescriptionId languageDescriptionId3 = new LanguageDescriptionId();
languageDescriptionId3.setId(languageDescriptionId);
languageDescriptionId3.setLanguageId(english.getId());
languageDescription3.setLanguageDescriptionId(languageDescriptionId3);
languageDescription3.setLanguage(english);
languageDescription3.setDescription("Turn Signal");
final LanguageDescription languageDescription4 = new LanguageDescription();
final LanguageDescriptionId languageDescriptionId4 = new LanguageDescriptionId();
languageDescriptionId4.setId(languageDescriptionId);
languageDescriptionId4.setLanguageId(german.getId());
languageDescription4.setLanguageDescriptionId(languageDescriptionId4);
languageDescription4.setLanguage(german);
languageDescription4.setDescription("Blinker");
final Article article2 = new Article();
article2.setId("a_2");
article2.setLanguageDescriptionId(languageDescriptionId);
}
}
可能会出现如下情况:
@Entity
public class Article {
@Id
private String id;
@Column(length = 9)
private String languageDescriptionId;
@OneToMany
@JoinColumn(name="ID", referencedColumnName="LANGUAGEDESCRIPTIONID", insertable=false, updatable=false)
private List<LanguageDescription> languageDescriptions;
}
现在上面的 OneToMany 不符合 JPA,因为 LanguageDescription 有一个复合 PK,所以 EclipseLink 和其他提供程序将抛出验证异常,这只是它看起来的一个例子。您将需要使其成为瞬态或添加另一个连接列,只是为了绕过验证,然后在描述符定制器中修改或添加映射。这在此处 jpa, eclips-link 2.5.1: OneToMany not working on columns not primary key
的答案中有所描述和显示