【问题标题】:Should @JoinTable be specified in both sides of a @ManyToMany relationship?@JoinTable 是否应该在 @ManyToMany 关系的两边都指定?
【发布时间】:2016-08-16 15:27:48
【问题描述】:

我有一个实体 Course 和一个实体 User。课程和用户之间存在多对多关系,因为一个课程可以有很多用户,一个用户可以注册很多课程。在这两个实体中,我都将@ManyToMany 注释放在特定字段上,即在Course 中我有:

@ManyToMany
private List<RegisteredUser> members;

User 我有:

@ManyToMany
private List<Course> coursesTaken;

现在,我知道这种多对多关系通常由第三张表表示。我也知道有注释@JoinTable 允许我们这样做。我不知道是否应该在两个不同实体的两个字段上添加此注释@JoinTable。顺便说一句,如果我需要同时添加,名称需要匹配吗?

【问题讨论】:

标签: java hibernate jpa many-to-many entity-relationship


【解决方案1】:

这实际上是一个好问题,它有助于理解“拥有”实体的概念,因为双方都不需要@JoinTable 注释。如果您想防止双方都有join tables,这是个好主意,那么您需要在一侧有一个mappedBy= 元素。 @JoinTable 注解用于指定表名或映射关联的列。

先看Javadoc for @JoinTable

指定关联的映射。它应用于关联的拥有方。

是否存在join table@ManyToMany 注释的mappedBy="name" 元素控制。 Javadoc for mappedBy for the ManyToMany annotation says

拥有关系的字段。除非关系是单向的,否则是必需的。

对于 Hibernate (5.0.9.Final) 中的(双向)示例,如果只有两个 @ManyToMany 注释并且没有 mappedBy= 元素,则默认将有两个 Entity 表和两个 Join Tables

Hibernate: create table Course (id bigint not null, primary key (id))
Hibernate: create table Course_Member (Course_id bigint not null, members_id bigint not null, primary key (Course_id, members_id))
Hibernate: create table Member (id bigint not null, primary key (id))
Hibernate: create table Member_Course (Member_id bigint not null, courses_id bigint not null, primary key (Member_id, courses_id))

虽然这是说每个实体“拥有”其ManyToMany 关系,但额外的join table 在典型用例中是多余的。但是,如果我决定让 Member 实体“拥有”该关系,那么我将 mappedBy= 元素添加到 Course 实体以指定它不拥有该关系:

@ManyToMany(mappedBy="courses")
Set<Member> members;

@JoinTable(name="Member_Course") 添加到Member 实体不会改变任何东西:它只是将表命名为与它本来应该命名的相同。

由于Course 实体不再拥有其ManyToMany 关系,因此不会创建额外的JoinTable

Hibernate: create table Course (id bigint not null, primary key (id))
Hibernate: create table Member (id bigint not null, primary key (id))
Hibernate: create table Member_Course (members_id bigint not null, courses_id bigint not null, primary key (members_id, courses_id))

这对开发人员很重要,因为他或她必须了解,除非将其添加到拥有实体(在本例中为 Member 实体),否则不会保留任何关系。但是,由于这是一种双向关系,开发人员应该将Course 添加到Member.coursesMemberCourse.members

因此,如果您有bidirectionalManyToMany 关系,这意味着您在所涉及的两个实体上都有ManyToMany,那么您应该在其中一个上添加mappedBy="name",以避免出现多余的join table。因为它是双向的,所以我认为你创建owning 实体的哪一方并不重要。与往常一样,启用 sql 日志并查看数据库中发生了什么总是一个好主意:

参考文献:

What is the difference between Unidirectional and Bidirectional associations?.

What does relationship owner means in bidirectional relationship?.

What is the “owning side” in an ORM mapping?.

Most efficient way to prevent an infinite recursion in toString()?.

【讨论】:

    【解决方案2】:

    您实际上可以在两边都使用@JoinTable,而且这通常很有意义!在我寻找这个解决方案数周后,我说的经验不足。

    尽管在整个互联网上,博客和文章都讲述了一个不同的故事 - JPA 的 Javadoc 很容易以这种方式被误解(或错误)。在一本面向专业人士的书中看到这个未注释的示例后,我尝试了它——它奏效了。

    怎么做:

    歌手乐器协会: 歌手方面

    @ManyToMany 
    @JoinTable(name = "singer_instrument", joinColumns =
    @JoinColumn(name = "SINGER_ID"), inverseJoinColumns = @JoinColumn(name = "INSTRUMENT_ID")) 
    public Set<Instrument> instruments;
    

    另一边完全一样! 仪器端

    @ManyToMany
    @JoinTable(name = "singer_instrument",
    joinColumns = @JoinColumn(name = "INSTRUMENT_ID"),
    inverseJoinColumns = @JoinColumn(name = "SINGER_ID"))
    public Set<Singer> singers;
    

    因此,如果您使用相同的名称处理同一个连接表“singer_instrument”,它就可以工作。 但是,如果您同时处理一个连接表“singer_instrument”和一个连接表“instrument-singer”,它将在数据库中产生两个不同的连接表。

    这很有意义,因为从数据库的角度来看,多对多关系没有拥有方。拥有方是指拥有关系的外键的一方。但是表“singer”和“instrument”都没有相互引用的外键。外键位于它们之间必要的连接表内。

    @JoinTable 在关系两边的优势: 假设一个歌手开始学习一种新乐器:您可以将乐器添加到歌手(反之亦然,因为它是双向的)并更新/合并歌手。更新将仅更新歌手和连接表。它不会碰仪器表

    现在另一种情况 - 吉他课程已经结束,所以你想删除吉他和以前的课程参与者/歌手之间的联系:从歌手那里移除乐器“吉他”之后(反之亦然!) ,您更新/合并仪器。更新将仅更新仪器和连接表。它不会碰歌手桌

    如果您只在一侧有@JoinTable,您将始终必须更新/保存/删除这一侧以安全地处理连接表中的条目(歌手和乐器之间的关系)。在这种情况下,您必须更新结束吉他课程的每位歌手。这没有正确反映关系类型,可能会导致性能问题数据事务期间的冲突

    【讨论】:

    • 这是可能的,尽管 @JoinTable 不应该这样使用,请参阅 documentation 11.1.27 JoinTable Annotation : > 在关联的拥有方指定了 JoinTable 注释。
    【解决方案3】:

    不。双方都有@ManyToMany,但只有一个有@JoinTable

    More ManyToMany info here

    【讨论】:

      【解决方案4】:

      要让@ManyToMany 处理现有模式(不是由 Hibernate 生成的),您必须在两个类上使用 @JoinTable 注释来指定表以及哪些列映射到相应类中的 Java 成员变量。我认为这个示例可以帮助您了解应该将哪些属性传递给注释:

      https://dzone.com/tutorials/java/hibernate/hibernate-example/hibernate-mapping-many-to-many-using-annotations-1.html

      【讨论】:

        猜你喜欢
        • 2020-04-10
        • 1970-01-01
        • 1970-01-01
        • 2020-05-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-28
        相关资源
        最近更新 更多