JPA JoinColumn vs mappedBy

有什么区别:

@Entity
public class Company {


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

而且

@Entity
public class Company {


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

注释@JoinColumn表示这个实体是关系的老板(即:对应的表有一个外键指向引用表的列),而属性mappedBy表示这一边的实体是关系的逆,所有者位于"other"实体。这也意味着你可以从你标注了" mappedbyquot;的类中访问另一个表。(完全双向关系)。

特别是,对于问题中的代码,正确的注释应该是这样的:

@Entity
public class Company {
@OneToMany(mappedBy = "company",
orphanRemoval = true,
fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private List<Branch> branches;
}


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

这个问题是关于在@OneToMany端使用@JoinColumn(罕见情况)。这里的点在物理信息复制(列名)和未优化的SQL查询,将产生一些额外的UPDATE语句中。

根据文档:

因为在JPA规范中多比一(几乎)总是双向关系中的业主方,所以一对多的关联由@OneToMany(mappedBy=...)注释

@Entity
public class Troop {
@OneToMany(mappedBy="troop")
public Set<Soldier> getSoldiers() {
...
}


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

Troop通过部队属性与Soldier有双向的一对多关系。您不必(也不可以)在mappedBy端定义任何物理映射。

要将一个双向的1映射到多个,使用一对多作为拥有方,您必须删除mappedBy元素,并将多个到一个的@JoinColumn设置为insertableupdatable为false。这个解决方案没有经过优化,并且会产生一些额外的UPDATE语句。

@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() {
...
}

注释的mappedBy理想情况下应该总是用于双向关系的父端(Company类),在这种情况下,它应该在Company类中指向子类(分支类)的成员变量' Company '

注释@JoinColumn用于指定加入实体关联的映射列,这个注释可以在任何类(父类或子类)中使用,但理想情况下它应该只在一边使用(无论是在父类或在子类中,而不是在两者中)在这种情况下,我在双向关系的子端(分支类)中使用它,指示分支类中的外键。

下面是工作示例:

母公司,公司

@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;
}




}

子类,分支

@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关系

单向多对单,无反向多对单,无连接表

不过它似乎只在JPA 2.x+中可用。当您希望子类只包含父类的ID,而不是完整的on引用时,这是非常有用的。

单向一对多关联

如果你使用@OneToMany注释和@JoinColumn,那么你就有一个单向的关联,就像下图中父实体Post和子实体PostComment之间的关联一样:

单向一对多关联

当使用单向一对多关联时,只有父端映射该关联。

在这个例子中,只有Post实体将定义@OneToMany与子实体PostComment的关联:

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "post_id")
private List<PostComment> comments = new ArrayList<>();

双向一对多关联

如果您使用@OneToManymappedBy属性集,您就有了一个双向关联。在我们的例子中,Post实体都有一个由PostComment子实体组成的集合,而子实体PostComment有一个指向父实体Post的引用,如下图所示:

双向一对多关联

PostComment实体中,post实体属性映射如下:

@ManyToOne(fetch = FetchType.LAZY)
private Post post;

我们显式地将fetch属性设置为FetchType.LAZY的原因是,默认情况下,所有@ManyToOne@OneToOne关联都是主动获取的,这可能会导致N+1个查询问题。

Post实体中,comments关联映射如下:

@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();

@OneToMany注释的mappedBy属性引用了子实体PostComment中的post属性,通过这种方式,Hibernate知道双向关联是由@ManyToOne端控制的,它负责管理表关系所基于的外键列值。

对于双向关联,你还需要有两个实用方法,比如addChildremoveChild:

public void addComment(PostComment comment) {
comments.add(comment);
comment.setPost(this);
}


public void removeComment(PostComment comment) {
comments.remove(comment);
comment.setPost(null);
}

这两种方法确保双向关联的双方是同步的。如果两端不同步,Hibernate不能保证关联状态更改会传播到数据库。

选择哪一个?

单向的@OneToMany关联性能不太好,所以应该避免使用它。

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

我不同意Óscar López这里接受的答案。这个答案是不准确的!

它不是@JoinColumn,这表明该实体是关系的所有者。相反,是@ManyToOne注释完成了这一点(在他的例子中)。

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

< p > < br > @JoinColumn

@JoinColumn的目的是创建一个连接列 不存在。如果是,则可以使用此注释 名称连接列。

< p > < br > 的MappedBy

MappedBy参数的目的是指示JPA: Do NOT 创建另一个连接表,因为已经映射了关系 这个关系的相反实体。< / p >
< p > < br >
记住:MappedBy是关系注释的一个属性,它的目的是生成一种机制来关联两个实体,默认情况下,它们通过创建一个连接表来实现。MappedBy在一个方向上停止进程

不使用MappedBy的实体被称为关系中的老板,因为映射机制是通过使用针对外键字段的三个映射注释之一在其类中指定的。这不仅指定了映射的性质,而且指示了连接表的创建。此外,还可以通过在外键上应用@JoinColumn注释来抑制连接表,从而将外键保存在所有者实体的表中。

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

为了说明MapppedBy是如何工作的,考虑下面的代码。如果要删除MappedBy参数,那么Hibernate实际上将创建两个连接表!为什么?因为在多对多关系中存在对称性,Hibernate没有理由选择一个方向而不是另一个方向。

因此,我们使用MappedBy告诉Hibernate,我们已经选择了其他< em > < / em >实体来指示两个实体之间关系的映射。

@Entity
public class Driver {
@ManyToMany(mappedBy = "drivers")
private List<Cars> cars;
}


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

在owner类中添加@JoinColumn(name = "driverID")(见下文),将防止创建连接表,相反,在Cars表中创建一个driverID外键列来构建映射:

@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)实体级别,它描述持久类,然后是(2)关系数据库级别,它假设实体映射到关系数据库,以及(3)java模型。

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

二级注释:@Table@Column@JoinColumn,… 如果您对JPA的默认值不满意,或者需要映射到现有数据库,则影响从实体/属性到关系数据库表/列的映射。这些注释可以被看作是实现注释,它们指定了映射应该如何完成

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

要回答这些问题:@OneToMany/mappedBy是最好的,因为它只使用来自实体域的注释。@oneToMany/@JoinColumn也很好,但它使用了一个实现注释,这并不是严格必要的。

让我简单点说。
不管映射如何,您都可以在两侧使用@JoinColumn
< / p >

我们把它分成三种情况 1)分公司到公司的单向映射。
2)公司到分公司的双向映射
3)只有公司到分公司的单向映射

所以任何用例都可以归入这三个类别。所以让我来解释一下如何使用@JoinColumn的mappedBy.
1)分公司到公司的单向映射。
在Branch表中使用JoinColumn
2)公司到分公司的双向映射
根据@Mykhaylo Adamovych的回答,在Company表中使用的mappedBy 3)公司到分公司的单向映射
在Company表中使用@JoinColumn即可
@Entity
public class Company {


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

这就是说,在基于外键“courseId”映射在分支表中,得到我所有分支的列表。注意:在这种情况下你不能从分支获取公司,从公司到分支只存在单向映射。

@Entity
public class Company {
  

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

这将给出下面的Hibernate日志

Hibernate: select nextval ('hibernate_sequence')
Hibernate: select nextval ('hibernate_sequence')
Hibernate: insert into company (name, company_id) values (?, ?)
Hibernate: insert into branch (company_id_ref, name, id) values (?, ?, ?)
Hibernate: update branch set company_id_ref=? where id=?

@Entity
public class Company {
  

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

这将给出下面的Hibernate日志

Hibernate: select nextval ('hibernate_sequence')
Hibernate: select nextval ('hibernate_sequence')
Hibernate: insert into company (name, company_id) values (?, ?)
Hibernate: insert into branch (company_id_ref, name, id) values (?, ?, ?)
我们可以清楚地看到@joinColumn将导致额外的更新查询。 所以你不需要显式地将父实体设置为子实体, 我们必须在使用mappedBy时这样做

.

.