在 JPA 中建模隐式多对多关系

Modeling implicit many to many relations in JPA

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

现在,我的第一个方法是对实体建模,例如

@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

的答案中有所描述和显示