本文閱讀時(shí)間3分鐘。由作者三汪首發(fā)于簡(jiǎn)書(shū)饮六。
幾個(gè)月前寫(xiě)了一篇《JPA實(shí)體關(guān)系映射:@ManyToMany多對(duì)多關(guān)系、@OneToMany@ManyToOne一對(duì)多多對(duì)一關(guān)系和@OneToOne的深度實(shí)例解析》,簡(jiǎn)要闡述了我對(duì)JPA的幾個(gè)實(shí)體關(guān)系映射的理解。
其中有關(guān)mappedBy的部分是這樣寫(xiě)的:
mappedBy聲明于關(guān)系的被維護(hù)方歇终,聲明的值為關(guān)系的維護(hù)方的關(guān)系對(duì)象屬性名。
在實(shí)例中逼龟,mappedBy被聲明于Course類(lèi)中评凝,其值為Student類(lèi)中的Set對(duì)象"courses"。即腺律,Student為關(guān)系維護(hù)方奕短,Course為被維護(hù)方。
這個(gè)表述其實(shí)是沒(méi)錯(cuò)的匀钧。但是我后面又補(bǔ)充了一句:
但是在實(shí)際操作中翎碑,我發(fā)現(xiàn)其實(shí)被維護(hù)方于維護(hù)方的概念并不那么重要。被維護(hù)方也可以對(duì)雙方關(guān)系進(jìn)行維護(hù)之斯。下面通過(guò)一組測(cè)試用例來(lái)進(jìn)行說(shuō)明日杈。
然而今天測(cè)試小哥給我提了個(gè)bug,讓我發(fā)現(xiàn)到其實(shí)mappedBy其實(shí)并非不重要的。
事實(shí)上這是一個(gè)很簡(jiǎn)單的bug莉擒。
通過(guò)查看日志酿炸,發(fā)現(xiàn)hibernate打印出來(lái)的sql中,不僅有update語(yǔ)句涨冀,還出現(xiàn)了delete語(yǔ)句梁沧。
這是因?yàn)榍岸藗鬟^(guò)來(lái)的數(shù)據(jù)里是不帶關(guān)聯(lián)對(duì)象的。
所以只要從數(shù)據(jù)庫(kù)里把關(guān)聯(lián)找出來(lái)然后賦給編輯的對(duì)象就解決了蝇裤。
像這樣:
修改前
@Override
@Transactional
public void saveLabelDict(LabelDictDomain labelDictDomain) {
if (labelDictDomain.getLabelType().equals(TYPE_UNIFORM)) {
if (StringUtil.isEmpty(labelDictDomain.getLabelGroup())) {
throw new BizException("統(tǒng)一標(biāo)簽標(biāo)簽分組不能為空廷支!");
}
}else if(!labelDictDomain.getLabelType().equals(TYPE_PERSONAL)){
throw new BizException("標(biāo)簽類(lèi)型非法!");
}
repository.save(labelDictDomain);
}
修改后
@Override
@Transactional
public void saveLabelDict(LabelDictDomain labelDictDomain) {
if (labelDictDomain.getLabelType().equals(TYPE_UNIFORM)) {
if (StringUtil.isEmpty(labelDictDomain.getLabelGroup())) {
throw new BizException("統(tǒng)一標(biāo)簽標(biāo)簽分組不能為空栓辜!");
}
}else if(!labelDictDomain.getLabelType().equals(TYPE_PERSONAL)){
throw new BizException("標(biāo)簽類(lèi)型非法恋拍!");
}
if (StringUtil.isNotEmpty(labelDictDomain.getId())) {
LabelDictDomain domain = repository.findOne(labelDictDomain.getId());
labelDictDomain.getMemberDomains().addAll(domain.getMemberDomains());
}
repository.save(labelDictDomain);
}
這個(gè)修改引發(fā)了我對(duì)于關(guān)系映射的思考。
因?yàn)樵谂c之相關(guān)的另外一個(gè)實(shí)體編輯時(shí)藕甩,并不需要這樣子手動(dòng)去配置關(guān)聯(lián)關(guān)系施敢。
相關(guān)代碼如下
@Override
@Transactional
public MemberDomain addMember(MemberDomain memberDomain) {
//無(wú)關(guān)代碼略
memberDomain.getBonusPointDomains().clear();
memberDomain.getFavoriteInfoDomains().clear();
memberDomain.getLabelDictDomains().clear();
memberDomain.setPointTotal(null);
memberDomain = repository.save(memberDomain);
return memberDomain;
}
在這里我不僅沒(méi)有手動(dòng)配置關(guān)聯(lián)關(guān)系,還為了維護(hù)數(shù)據(jù)安全狭莱,把其他實(shí)體關(guān)聯(lián)給清空了僵娃。
但是這里卻不會(huì)產(chǎn)生delete語(yǔ)句影響數(shù)據(jù)庫(kù)里的內(nèi)容。
這是為什么呢腋妙。
在看了一眼實(shí)體中的關(guān)聯(lián)配置以后默怨,我突然恍然大悟。
其實(shí)都是因?yàn)檫@個(gè)小小的沒(méi)有被注意到的mappedBy
啊骤素。
因?yàn)?code>LabelDictDomain中的memberDomains
對(duì)象在MemberDomain
中被配置為了關(guān)系的維護(hù)方匙睹。
所以labelDictDomains
可以為空,而memberDomains
一定不能為空。
否則就會(huì)出現(xiàn)關(guān)聯(lián)關(guān)系被清空的情況济竹。
也就是說(shuō)痕檬,被維護(hù)方不會(huì)主動(dòng)去維護(hù)關(guān)聯(lián)關(guān)系。
真正的關(guān)系維護(hù)送浊,掌握在維護(hù)方的手中梦谜。
關(guān)聯(lián)配置代碼如下
被維護(hù)方:
@ManyToMany(mappedBy = "memberDomains", cascade={CascadeType.MERGE,CascadeType.DETACH} , fetch = FetchType.EAGER)
private Set<LabelDictDomain> labelDictDomains = new HashSet<>();
維護(hù)方:
@ManyToMany(cascade={CascadeType.DETACH} , fetch = FetchType.LAZY)
@JoinTable(name="member_label",joinColumns = { @JoinColumn(name="LABEL_ID")},inverseJoinColumns = @JoinColumn(name="MEMBER_ID"))
private Set<MemberDomain> memberDomains = new HashSet<>();
以上。
希望我的文章對(duì)你能有所幫助袭景。
我不能保證文中所有說(shuō)法的百分百正確唁桩,但我能保證它們都是我的理解和感悟以及拒絕復(fù)制黏貼。
有什么意見(jiàn)浴讯、見(jiàn)解或疑惑朵夏,歡迎留言討論。