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

考虑我想在JPA中表示的以下遗留数据模型:

  • 语言有
    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;
}