关于Java:使用JPA @OneToMany关联时@JoinColumn和maptedBy有什么区别

What's the difference between @JoinColumn and mappedBy when using a JPA @OneToMany association

之间有什么区别?

1
2
3
4
5
6
7
8
@Entity
public class Company {

    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
    @JoinColumn(name ="companyIdRef", referencedColumnName ="companyId")
    private List<Branch> branches;
    ...
}

1
2
3
4
5
6
7
@Entity
public class Company {

    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY, mappedBy ="companyIdRef")
    private List<Branch> branches;
    ...
}


注释@JoinColumn表示此实体是关系的所有者(即:对应的表具有一列,该列带有被引用表的外键),而属性mappedBy表示该方的实体是关系的所有者。与关系相反,所有者位于"其他"实体中。这也意味着您可以从用" mappedBy"(完全双向关系)注释的类中访问另一个表。

特别是,对于问题中的代码,正确的注释应如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
@Entity
public class Company {
    @OneToMany(fetch = FetchType.LAZY, mappedBy ="company")
    private List<Branch> branches;
}

@Entity
public class Branch {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name ="companyId")
    private Company company;
}


@JoinColumn可以在关系的两侧使用。问题是关于在@OneToMany侧使用@JoinColumn(罕见情况)。这里的重点是物理信息重复(列名)以及未优化的SQL查询,这将产生一些其他的UPDATE语句。

根据文档:

由于JPA规范中的一对多关系(几乎)始终是双向关系的所有者,因此,@ OneToMany(mappedBy = ...)注释了一对多关联。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Entity
public class Troop {
    @OneToMany(mappedBy="troop")
    public Set<Soldier> getSoldiers() {
    ...
}

@Entity
public class Soldier {
    @ManyToOne
    @JoinColumn(name="troop_fk")
    public Troop getTroop() {
    ...
}

部队通过部队属性与士兵建立了双向的一对多关系。您不必(不必)在mapledBy端定义任何物理映射。

要以一对多方为拥有方映射双向一对多,您必须删除mapledBy元素,并将多对一设置为@JoinColumn,以将其可插入,并将其可更新为false。此解决方案未经过优化,将产生一些其他的UPDATE语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Entity
public class Troop {
    @OneToMany
    @JoinColumn(name="troop_fk") //we need to duplicate the physical information
    public Set<Soldier> getSoldiers() {
    ...
}

@Entity
public class Soldier {
    @ManyToOne
    @JoinColumn(name="troop_fk", insertable=false, updatable=false)
    public Troop getTroop() {
    ...
}


正如我在本文中所解释的,如果将@OneToMany注释与@JoinColumn一起使用,则您将具有单向关联。

如果将@OneToManymappedBy属性一起使用,则将具有双向关联,这意味着需要在mappedBy引用的子方面具有@ManyToOne关联。

单向@OneToMany关联不能很好地执行,因此应避免使用它。

您最好使用效率更高的双向@OneToMany


理想情况下,始终应在双向关系的父级(公司类)中使用mapledBy注释,在这种情况下,注释应在公司类中指向子类(分支类)的成员变量" company"

注释@JoinColumn用于指定用于连接实体关联的映射列,该注释可以在任何类(父类或子类)中使用,但理想情况下应仅在一侧使用(在父类或子类中不使用)在这两种情况下,在这种情况下,我在双向关系的子级(分支类)中使用了它,指示了分支类中的外键。

以下是工作示例:

家长班

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Entity
public class Company {


    private int companyId;
    private String companyName;
    private List<Branch> branches;

    @Id
    @GeneratedValue
    @Column(name="COMPANY_ID")
    public int getCompanyId() {
        return companyId;
    }

    public void setCompanyId(int companyId) {
        this.companyId = companyId;
    }

    @Column(name="COMPANY_NAME")
    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    @OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy="company")
    public List<Branch> getBranches() {
        return branches;
    }

    public void setBranches(List<Branch> branches) {
        this.branches = branches;
    }


}

子班,分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Entity
public class Branch {

    private int branchId;
    private String branchName;
    private Company company;

    @Id
    @GeneratedValue
    @Column(name="BRANCH_ID")
    public int getBranchId() {
        return branchId;
    }

    public void setBranchId(int branchId) {
        this.branchId = branchId;
    }

    @Column(name="BRANCH_NAME")
    public String getBranchName() {
        return branchName;
    }

    public void setBranchName(String branchName) {
        this.branchName = branchName;
    }

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="COMPANY_ID")
    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }


}


我想补充一下,@JoinColumn不一定总是与物理信息位置相关,如该答案所示。即使父表没有指向子表的表数据,也可以将@JoinColumn@OneToMany组合。

如何在JPA中定义单向OneToMany关系

单向OneToMany,Noverse ManyToOne,无联接表

不过,它似乎仅在JPA 2.x+中可用。对于希望子类仅包含父级ID而不是完整引用的情况,这很有用。


我不同意óscarLópez在这里所接受的答案。这个答案不正确!

不是@JoinColumn表示此实体是关系的所有者。取而代之的是@ManyToOne注释(在他的示例中)。

诸如@ManyToOne@OneToMany@ManyToMany之类的关系注释告诉JPA / Hibernate创建映射。默认情况下,这是通过单独的联接表完成的。

@JoinColumn

The purpose of @JoinColumn is to create a join column if one does
not already exist. If it does, then this annotation can be used to
name the join column.

映射

The purpose of the MappedBy parameter is to instruct JPA: Do NOT
create another join table as the relationship is already being mapped
by the opposite entity of this relationship.

请记住:MappedBy是关系注释的属性,其目的是生成一种机制,以关联两个实体,默认情况下,它们通过创建联接表来实现此功能。 MappedBy停止向一个方向进行处理。

之所以说不使用MappedBy的实体是关系的所有者,是因为映射的机制是通过使用针对外键字段的三个映射注释之一在其类内规定的。这不仅指定了映射的性质,还指示了联接表的创建。此外,通过在外键上应用@JoinColumn批注,也可以抑制联接表,该选项将其保留在所有者实体的表内。

因此,总而言之:@JoinColumn要么创建一个新的联接列,要么重命名一个现有的联接列。而MappedBy参数与另一个(子)类的关系注释协同工作,以便通过连接表或通过在所有者实体的关联表中创建外键列来创建映射。

为了说明MapppedBy的工作方式,请考虑以下代码。如果要删除MappedBy参数,则Hibernate实际上将创建两个联接表!为什么?因为在多对多关系中存在对称性,并且Hibernate没有选择一个方向胜过另一个方向的理由。

因此,我们使用MappedBy告诉Hibernate,我们选择了另一个实体来规定两个实体之间的关系映射。

1
2
3
4
5
6
7
8
9
10
11
@Entity
public class Driver {
    @ManyToMany(mappedBy ="drivers")
    private List<Cars> cars;
}

@Entity
public class Cars {
    @ManyToMany
    private List<Drivers> drivers;
}

在所有者类中添加@JoinColumn(name =" driverID")(请参见下文)将阻止创建联接表,而是在Cars表中创建driverID外键列以构建映射:

1
2
3
4
5
6
7
8
9
10
11
12
@Entity
public class Driver {
    @ManyToMany(mappedBy ="drivers")
    private List<Cars> cars;
}

@Entity
public class Cars {
    @ManyToMany
    @JoinColumn(name ="driverID")
    private List<Drivers> drivers;
}


JPA是一个分层的API,不同级别具有各自的注释。最高级别是(1)描述持久性类的Entity级别,然后您具有(2)关系数据库级别,其中假定实体已映射到关系数据库,以及(3)Java模型。

1级注释:@ Entity,@ Id,@ OneToOne,@ OneToMany,@ ManyToOne,@ ManyToMany。
您可以仅使用这些高级注释在应用程序中引入持久性。但是随后您必须根据JPA所做的假设来创建数据库。这些注释指定实体/关系模型。

2级注释:@ Table,@ Column,@ JoinColumn,...
如果您对JPA的默认设置不满意或需要映射到现有数据库,请影响从实体/属性到关系数据库表/列的映射。这些注释可视为实现注释,它们指定应如何完成映射。

在我看来,最好尽可能地坚持高级注释,然后根据需要引入低级注释。

回答问题:@ OneToMany / mappedBy最好,因为它仅使用来自实体域的注释。 @ oneToMany / @ JoinColumn也很好,但是在并非严格必要的地方使用了实现注释。