JPA联接列与Map者

qeeaahzv  于 2022-11-14  发布在  其他
关注(0)|答案(9)|浏览(163)

有什么区别:

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

arknldoa1#

注解@JoinColumn指示此实体是关系的 * 所有者 *(即:对应的表有一个列,该列有指向被引用表的外键),而属性mappedBy指示此端的实体是关系的逆,所有者驻留在“other”实体中。这也意味着可以从用“mappedBy”注解的类访问另一个表(完全双向关系)。
特别是,对于问题中的代码,正确的注解应该如下所示:

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

oxiaedzo2#

**@JoinColumn可用于关系的两端。**问题是关于在@OneToMany端使用@JoinColumn(罕见情况)。此处的要点是 * 物理信息重复 *(列名)沿着 * 未优化的SQL查询,该查询将生成一些额外的UPDATE语句 *。

根据documentation
由于在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() {
    ...
}

TroopSoldier之间是双向的一对多关系,通过roop属性,不需要在mappedBy端定义任何物理Map。
若要Map双向一对多,并将一对多方作为拥有方,必须删除mappedBy元素,并将多对一个@JoinColumn设置为insertable,将updatable设置为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() {
    ...
}
kd3sttzy

kd3sttzy3#

单向一对多关联

如果将@OneToMany注解与@JoinColumn一起使用,则具有单向关联,如下图中的Post父实体和PostComment子实体之间的关联:

使用单向一对多关联时,只有父端Map该关联。
在此示例中,只有Post实体会定义@OneToManyPostComment子实体的关联:

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

双向一对多关联

如果您使用@OneToManymappedBy属性集,则具有双向关联。在我们的示例中,Post实体具有PostComment子实体的集合,并且子PostComment实体具有对父Post实体的引用,如下图所示:

PostComment实体中,post实体属性Map如下:

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

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

@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,因为它效率更高。

ukxgm1gy

ukxgm1gy4#

我不同意奥斯卡·洛佩斯的公认答案,这个答案是不准确的!
不是@JoinColumn指示该实体是关系的所有者,而是@ManyToOne注解(在他的示例中)。
@ManyToOne@OneToMany@ManyToMany等关系注解告诉JPA/Hibernate**创建一个Map。**默认情况下,这是通过一个单独的连接表完成的。

@联接列

@JoinColumn的用途是创建一个 * 联接列 *(如果不存在)。如果存在,则此注解可用于 * 命名 * 联接列。

Map者

MappedBy参数的用途是指示JPA:请勿创建另一个联接表,因为此关系的“相反”实体已Map此关系。
请记住:MappedBy是关系注解的一个属性,其目的是生成一种机制来关联两个实体,默认情况下,它们通过创建一个连接表来关联两个实体。MappedBy在一个方向上停止该过程。
不使用MappedBy的实体被认为是关系的 * 所有者 ,因为Map的机制是通过对外键字段使用三个Map注解中的一个在其类中指定的。这不仅指定了Map的性质,而且还指示了连接表的创建。此外,通过在外键上应用@JoinColumn注解也存在抑制连接表的选项,该注解将外键保留在所有者实体的表内。
总结一下:@JoinColumn创建新的联接列或重命名现有的联接列;而X1 M1 N1 X参数与另一(子)类的关系注解协作工作,以便通过连接表或通过在所有者实体的关联表中创建外键列来创建Map。
为了说明MapppedBy是如何工作的,请考虑下面的代码。如果删除MappedBy参数,那么Hibernate实际上会创建两个连接表!为什么?因为多对多关系中存在对称性,Hibernate没有理由选择一个方向而不是另一个方向。
因此,我们使用MappedBy来告诉Hibernate,我们已经选择了
另一个*实体来指示两个实体之间关系的Map。

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

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

在所有者类(见下文)中添加@JoinColumn(name =“driverID”)将阻止创建连接表,而是在汽车表中创建driverID外键列来构造Map:

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

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

lmvvr0a85#

注解mappedBy理想情况下应始终用于双向关系的父类(Company类),在这种情况下,它应位于指向子类(分支类)成员变量“company”的Company类中
注解**@JoinColumn**用于指定用于连接实体关联的Map列,此注解可用于任何类(父类或子类),但理想情况下应仅在一侧使用(父类或子类,而不是在两者中)。在本例中,我在双向关系的子类侧(分支类)使用了此注解,以指示分支类中的外键。
下面是工作示例:

父类,公司

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

}
unguejic

unguejic6#

我只想补充一点,@JoinColumn并不一定要像this答案所建议的那样与 * 物理信息位置 * 相关,即使父表没有指向子表的表数据,也可以将@JoinColumn@OneToMany组合在一起。


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

它似乎只在JPA 2.x+中可用。当你希望子类只包含父类的ID,而不是一个完整的on引用时,它很有用。

hmmo2u0o

hmmo2u0o7#

让我简单点说。
您可以在任何一端使用**@JoinColumn**,而不考虑Map。
让我们把它分成三种情况。
1)从分支到公司的单向Map。
2)从公司到分支的双向Map。
3)仅限从公司到分支的单向Map。
所以任何用例都属于这三个类别。所以让我来解释一下如何使用**@JoinColumnmappedBy**。
1)从分支到公司的单向Map。
在分支表中使用JoinColumn
2)从公司到分支的双向Map。
在Company表中使用mappedBy,如@Mykhaylo Adamovych的答案所述。
3)公司到分支的单向Map。
只需在Company表中使用**@JoinColumn**即可。

@Entity
public class Company {

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

这表示在基于外键“courseId”Map的branches表中,获取所有分支的列表。注意:在这种情况下,您不能从分支获取公司,从公司到分支机构只存在单向Map。

klr1opcd

klr1opcd8#

JPA是一个分层的API,不同的层有自己的注解。最高层是(1)实体层,它描述持久类,然后是(2)关系数据库层,它假设实体被Map到关系数据库,以及(3)Java模型。
1级注解:@Entity@Id@OneToOne@OneToMany@ManyToOne@ManyToMany。您可以单独使用这些高级注解在应用程序中引入持久性。但之后您必须根据JPA所做的假设创建数据库。这些注解指定实体/关系模型。
二级注解:@Table@Column@JoinColumn,...如果您不满意JPA的预设值,或需要Map至现有数据库,则会影响从实体/属性到相关式数据库表格/栏的Map。这些注解可视为实作注解,它们指定Map的执行方式。
在我看来,最好是尽可能多地坚持高级别的注解,然后根据需要引入低级别的注解。
要回答这些问题:@OneToMany/mappedBy是最好的,因为它只使用来自实体域的注解。@oneToMany/@JoinColumn也很好,但它使用了一个实现注解,而这并不是严格必要的。

l2osamch

l2osamch9#

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

这将给予以下休眠日志

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将子实体与父实体一起保存时必须执行的操作

相关问题