本文基于 SpringBoot + Hibernate 下的 JPA
總的來說庆聘,就是在 @OneToMany
和 @ManyToOne
注解的基礎(chǔ)上結(jié)合使用兩者胜臊。下面以 Lead
和 Attachment
兩個(gè)實(shí)體為例進(jìn)行說明,即一個(gè) Lead
和多個(gè) Attachment
雙向關(guān)聯(lián)伙判。
@Entity
@Table(name = "lead")
public class Lead {
@OneToMany
private List<Attachment> attachmentList = new ArrayList<>();
}
@Entity
@Table(name = "attachment")
public class Attachment {
@ManyToOne
private Lead lead;
}
在 One
端象对,即 Lead
中使用 @OneToMany
注解,而在 Many
端宴抚,即 Attachment
中使用 @ManyToOne
注解勒魔。
關(guān)聯(lián)媒介
我們知道關(guān)系的關(guān)聯(lián)媒介有兩種
- 通過中間表關(guān)聯(lián),由
@JoinColumn
配置 - 通過外鍵關(guān)聯(lián)菇曲,由
@JoinTable
配置
而在雙向關(guān)系中冠绢,這兩個(gè)注解加在 @OneToMany
端才有效,即此時(shí)關(guān)聯(lián)媒介由 One
端決定常潮。下面以 @JoinColumn
為例弟胀,其 name 屬性指定了 Many
端表中生成的外鍵的字段名。
@OneToMany
@JoinColumn(name="lead_id")
private List<Attachment> attachmentList = new ArrayList<>();
@ManyToOne
private Lead lead;
如果 @OneToMany
沒有這兩個(gè)注解喊式,則按照 @OneToMany
單向時(shí)的默認(rèn)情況孵户, Hibernate 會(huì)自動(dòng)生成中間表。
關(guān)系維護(hù)
在上述的配置下岔留,關(guān)聯(lián)關(guān)系由兩端同時(shí)進(jìn)行維護(hù)夏哭,這樣會(huì)產(chǎn)生額外的 update
語句。解決辦法就是 One
端將維護(hù)權(quán)交由 Many
端献联,通過 @OneToMany
的 mappedBy
屬性實(shí)現(xiàn), 其值為 Many
端實(shí)體中關(guān)系對(duì)應(yīng)的字段名竖配,這里為 lead
厕吉。
@OneToMany(mappedBy = "lead"))
private List<Attachment> attachmentList = new ArrayList<>();
@ManyToOne
private Lead lead;
由于 mappedBy
屬性和 @JoinColumn
互斥,所以 @OneToMany
不能再加 @JoinColumn
, 何況此時(shí)的關(guān)聯(lián)方式轉(zhuǎn)由 @ManyToOne
決定械念,所以要實(shí)現(xiàn)指定外鍵關(guān)聯(lián)头朱,則給 @ManyToOne
添加 @JoinColumn
。
@OneToMany(mappedBy = "lead"))
private List<Attachment> attachmentList = new ArrayList<>();
@ManyToOne
@JoinColumn(name="lead_id")
private Lead lead;
Many 端的懶加載
上述配置下龄减,還會(huì)產(chǎn)生另外一個(gè)問題项钮。假設(shè)有一個(gè)孤立 Attachment
存在數(shù)據(jù)庫中,即沒有關(guān)聯(lián)任何 Lead
希停。此時(shí)通過 JPA 接口的 findById
方法是找不到這個(gè) Attachment
的烁巫。因?yàn)?@ManyToOne
的級(jí)聯(lián)查詢默認(rèn)是饑餓獲取,查找的同時(shí)會(huì)與 Lead
進(jìn)行 join宠能,而由于關(guān)聯(lián)關(guān)系還不存在亚隙,所以自然找不到。解決辦法就是將 @ManyToOne
的 fetch
屬性設(shè)置為 FetchType.LAZY
违崇,即懶加載阿弃。
@OneToMany(mappedBy = "lead"))
private List<Attachment> attachmentList = new ArrayList<>();
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="lead_id")
private Lead lead;