有什么区别:

@Entity
public class Company {

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

and

@Entity
public class Company {

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

当前回答

JPA是一个分层的API,不同的级别有自己的注释。最高级别是(1)实体级别,它描述持久类,然后是(2)关系数据库级别,它假设实体映射到关系数据库,以及(3)java模型。

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

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

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

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

其他回答

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


}
@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=?

And

@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时要做的 拯救有父母陪伴的孩子

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

它不是@JoinColumn,这表明该实体是关系的所有者。取而代之的是@ManyToOne注释(在他的例子中)。

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

@JoinColumn

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

的MappedBy

MappedBy参数的目的是指示JPA: Do NOT 创建另一个连接表,因为已经映射了关系 通过这种关系的相反实体。

请记住:MappedBy是关系注释的一个属性,其目的是生成一种机制来关联两个实体,默认情况下,它们通过创建一个连接表来实现。MappedBy在一个方向上停止这个过程。

The entity not using MappedBy is said to be the owner of the relationship because the mechanics of the mapping are dictated within its class through the use of one of the three mapping annotations against the foreign key field. This not only specifies the nature of the mapping but also instructs the creation of a join table. Furthermore, the option to suppress the join table also exists by applying @JoinColumn annotation over the foreign key which keeps it inside the table of the owner entity instead.

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

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

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

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

@JoinColumn可以在关系的双方使用。问题是关于在@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() {
    ...
} 

部队通过部队属性与士兵产生双向多的关系。您不必(一定不)在mappedBy端中定义任何物理映射。

要将一个双向的1映射到多,将一对多的端映射为所属端,您必须删除mappedBy元素,并将多对一的@JoinColumn设置为可插入的,并将可更新的设置为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() {
    ...
}

单向一对多关联

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

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

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

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

双向一对多关联

如果您使用带有mappedBy属性集的@OneToMany,那么您就有了一个双向关联。在我们的例子中,Post实体都有一个PostComment子实体的集合,并且子PostComment实体有一个指向父Post实体的引用,如下图所示:

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

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

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

在Post实体中,评论关联映射如下:

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

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

对于双向关联,你还需要有两个实用工具方法,比如addChild和removecchild:

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。