JPA中隐式多对多关系的建模
考虑我想在JPA中表示的以下遗留数据模型:JPA中隐式多对多关系的建模,jpa,eclipselink,Jpa,Eclipselink,考虑我想在JPA中表示的以下遗留数据模型: 语言有id和name 语言描述有一个复合主键,由id和Language\u id组成,另外还有一个description,这样对于每种翻译,每种语言都有一个条目 文章有(国际化的)描述,由前面提到的languagedescription\u id标识 现在,我的第一种方法是对实体建模,如 @Entity public class Language { @Id @Column(length = 3) private Stri
- 语言有
和id
name
- 语言描述有一个复合主键,由
和id
组成,另外还有一个Language\u id
,这样对于每种翻译,每种语言都有一个条目description
- 文章有(国际化的)描述,由前面提到的
标识languagedescription\u 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
表中,尽管article仅引用languagedescription\u id
以防止多个冗余条目。将文章
实体替换为
@Entity
public class Article {
@Id
private String id;
@ManyToMany
private List<LanguageDescription> languageDescriptions;
}
省略JPA支持并在@EntityListener
中获取翻译
现在,这种情况在JPA中是可以自动管理的,还是JPA中根本不支持这种没有映射表的“隐式”多对多关系
例子
可能出现以下情况:
@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;
}
@实体
公共类文章{
@身份证
私有字符串id;
@列(长度=9)
私有字符串语言DescriptionID;
@独身癖
@JoinColumn(name=“ID”,referencedColumnName=“LANGUAGEDESCRIPTIONID”,insertable=false,updateable=false)
私有列表语言描述;
}
现在,上面的OneToMany不符合JPA,因为LanguageDescription有一个复合PK,所以EclipseLink和其他提供商将抛出验证异常,这只是一个示例。为了绕过验证,您需要将其设置为瞬态或添加另一个联接列,然后在描述符定制器中修改或添加映射。这里的答案对此进行了描述和显示您的数据库模型不清楚。为什么语言LanguageDescription有一个复合PK,LanguageDescriptionId.id不是唯一的吗?它是作为文章的外键,还是如何设置/确定其值?许多映射使用联接表,但您可以指定一个带有joincolumn的OneToMany。JPA强制pk转到主键,虽然建议将其用于缓存和许多性能原因,但EclipseLink在映射中支持FK到非pk字段。“为什么要为language LanguageDescription设置复合pk,LanguageDescriptionId.id不是唯一的吗?”-否,一个LanguageDescription ID映射到一个翻译列表,通过LanguageDescription ID和language ID的组合,每种语言的翻译都是唯一的。“它是作为文章的外键,还是它的值是如何设置/确定的?”-项目在遗留数据模型中具有LanguageDescription ID,并指向一组LanguageDescription。另外,@OneToMany不适合,因为翻译可以在文章中重复使用,我不想创建额外的联接表。您需要解释或显示您的表,因为不清楚这与翻译的关系。也不清楚您计划如何设置languageDescriptionId字段和“修改”列表。我认为您的第二个选择是,在文章中为languageDescriptionId使用字符串,这是您最好的选择,然后只查询与该languageId匹配的关联LanguageDescription的集合是您最好的选择,而不是向您的模型中添加复杂和人为的多个规则。在模型中缓存关系并不总是最好的解决方案,因为您可以直接查询它们。为了完整起见,我添加了一个示例来描述我的问题。可以看出,我的两篇文章分别有一个英文和德文译本,而文章和译文之间的唯一关系是具有相同的格式
languageDescriptionId
但是没有对象关系-现在,当一个人想知道其中一篇文章的翻译时,他必须获得所有languageDescriptionId
与文章相同的languageDescriptionId
对象,并用所需语言过滤其中一个。如前所述,article->LanguageDescription关系是1:M关系,而不是多对多关系,因为该文章具有LanguageDescription实例引用的单个languageDescriptionId字符串值。翻译是如何进入它的,为什么它需要一个联接表?好的,谢谢-因为正确的翻译现在确实是暂时的,并且通过@EntityListener
使用用户会话的语言来确定目标翻译,我将接受你的回答。
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;
}