JPA實(shí)體關(guān)系映射:@ManyToMany多對(duì)多關(guān)系试幽、@OneToMany@ManyToOne一對(duì)多多對(duì)一關(guān)系和@OneToOne的深度實(shí)例解析筝蚕。

本文由作者三汪首發(fā)于簡(jiǎn)書(shū)。


為什么要有實(shí)體關(guān)系映射

答:簡(jiǎn)化編程操作铺坞。把冗余的操作交給底層框架來(lái)處理起宽。
例如,如果我要給一位新入學(xué)的學(xué)生添加一位新的老師济榨。而這個(gè)老師又是新來(lái)的坯沪,在學(xué)生數(shù)據(jù)庫(kù)與教師數(shù)據(jù)庫(kù)中均不存在對(duì)應(yīng)的數(shù)據(jù)。那么我需要先在教師數(shù)據(jù)庫(kù)中保存新來(lái)的老師的數(shù)據(jù)擒滑,同時(shí)在學(xué)生數(shù)據(jù)庫(kù)中保存新學(xué)生的數(shù)據(jù)腐晾,然后再給兩者建立關(guān)聯(lián)。
而如果我們使用了實(shí)體關(guān)系映射丐一,我們只需要將該新教師實(shí)體交給該學(xué)生實(shí)體藻糖,然后保存該學(xué)生實(shí)體即可完成。

什么是多對(duì)多關(guān)系

多對(duì)多關(guān)系是關(guān)系數(shù)據(jù)庫(kù)中兩個(gè)表之間的一種關(guān)系库车, 該關(guān)系中第一個(gè)表中的一個(gè)行可以與第二個(gè)表中的一個(gè)或多個(gè)行相關(guān)巨柒。第二個(gè)表中的一個(gè)行也可以與第一個(gè)表中的一個(gè)或多個(gè)行相關(guān)。
如果我們通過(guò)學(xué)生與課程的關(guān)系來(lái)說(shuō)明多對(duì)多關(guān)系:一位學(xué)生柠衍,會(huì)修多門(mén)課程洋满;而一門(mén)課程,也會(huì)被多位學(xué)生修習(xí)珍坊。此時(shí)牺勾,雙方的關(guān)系即為多對(duì)多關(guān)系。
擁有多對(duì)多關(guān)系的兩個(gè)實(shí)體將會(huì)有一個(gè)中間表來(lái)記錄兩者之間的關(guān)聯(lián)關(guān)系垫蛆。
下面禽最,我們來(lái)建立實(shí)體腺怯。

Studnt實(shí)體

package com.wolfgy.domain;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;

import org.hibernate.annotations.GenericGenerator;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@NoArgsConstructor
@Getter
@Setter
public class Student {

    @Id
    @GeneratedValue(generator = "idGenerator")
    @GenericGenerator(name = "idGenerator", strategy = "uuid")
    private String id;
    private String sName;
    @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
    private Set<Course> courses = new HashSet<>();
}

Course實(shí)體

package com.wolfgy.domain;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;

import org.hibernate.annotations.GenericGenerator;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity
@NoArgsConstructor
@Getter
@Setter
public class Course {
    @Id
    @GeneratedValue(generator = "idGenerator")
    @GenericGenerator(name = "idGenerator", strategy = "uuid")
    private String id;
    private String cName;
    @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY,mappedBy="courses")
    private Set<Student> students= new HashSet<>();
}

@ManyToMany注解說(shuō)明:

如代碼所示袱饭,在兩個(gè)實(shí)體中,我們都使用了@ManyToMany這一注解呛占。
這一注解表明虑乖,當(dāng)前實(shí)體為多對(duì)多關(guān)系的其中一端。

注解可以在Collection晾虑、Set疹味、List仅叫、Map上使用,我們可以根據(jù)業(yè)務(wù)需要選擇糙捺。
Collection類是Set和List的父類诫咱,在未確定使用Set或List時(shí)可使用;
Set集合中對(duì)象不能重復(fù)洪灯,并且是無(wú)序的;
List集合中的對(duì)象可以有重復(fù)坎缭,并且可以有排序;
Map集合是帶有key和value值的集合签钩。

同時(shí)掏呼,我們聲明的集合需要進(jìn)行初始化。
如Collection可以初始化為ArrayList或HashSet铅檩;
Set可以初始化為HashSet憎夷;
List可以初始化為ArrayList;
Map可以初始化為HashMap昧旨。

在注解中拾给,我們可以設(shè)置cascade(級(jí)聯(lián)關(guān)系),fetch(加載策略),mappedBy(聲明關(guān)系的維護(hù)方)等屬性兔沃。
關(guān)于級(jí)聯(lián)關(guān)系可以在我的這篇文章中了解: ==》戳這里
我們簡(jiǎn)要介紹一下mappedBy鸣戴。

mappedBy聲明于關(guān)系的被維護(hù)方,聲明的值為關(guān)系的維護(hù)方的關(guān)系對(duì)象屬性名粘拾。
在實(shí)例中窄锅,mappedBy被聲明于Course類中,其值為Student類中的Set對(duì)象"courses"缰雇。即入偷,Student為關(guān)系維護(hù)方,Course為被維護(hù)方械哟。

但是在實(shí)際操作中疏之,我發(fā)現(xiàn)其實(shí)被維護(hù)方于維護(hù)方的概念并不那么重要。被維護(hù)方也可以對(duì)雙方關(guān)系進(jìn)行維護(hù)暇咆。下面通過(guò)一組測(cè)試用例來(lái)進(jìn)行說(shuō)明锋爪。
(關(guān)于mappedBy,我又更新了一篇補(bǔ)遺,建議閱讀。閱讀時(shí)間3分鐘 ==》戳這里)
測(cè)試用例

    /**
     * 僅將被維護(hù)方對(duì)象添加進(jìn)維護(hù)方對(duì)象Set中
     * 保存維護(hù)方對(duì)象
     */
    @Test
    public void 多對(duì)多插入1() {
        Student s = new Student();
        s.setSName("二狗");
        Course c = new Course();
        c.setCName("語(yǔ)文");
        s.getCourses().add(c);
        studentService.save(s);
    }
    
    /**
     * 僅將維護(hù)方對(duì)象添加進(jìn)被維護(hù)方對(duì)象Set中
     * 保存被維護(hù)方對(duì)象
     */
    @Test
    public void 多對(duì)多插入2() {
        Student s = new Student();
        s.setSName("三汪");
        Course c = new Course();
        c.setCName("英語(yǔ)");
        c.getStudents().add(s);
        courseService.save(c);
    }
    
    /**
     * 將雙方對(duì)象均添加進(jìn)雙方Set中
     * 保存被維護(hù)方對(duì)象
     */
    @Test
    public void 多對(duì)多插入3() {
        Student s = new Student();
        s.setSName("一晌");
        Course c = new Course();
        c.setCName("數(shù)學(xué)");
        s.getCourses().add(c);
        c.getStudents().add(s);
        courseService.save(c);
    }

    /**
     * 刪除維護(hù)方對(duì)象
     */
    @Test
    public void 多對(duì)多刪除1(){
        Student s = studentService.findByName("二狗");
        studentService.delete(s);
    }

    /**
     * 刪除被維護(hù)方對(duì)象
     */
    @Test
    public void 多對(duì)多刪除2(){
        //Course c = courseService.findByName("英語(yǔ)");
        Course c = courseService.findByName("數(shù)學(xué)");
        courseService.delete(c);
    }
測(cè)試說(shuō)明及結(jié)果:

在上面的測(cè)試用例中爸业,我們進(jìn)行了三次不同的保存和三次不同的保存刪除操作(多對(duì)多刪除2中分別進(jìn)行了兩次刪除操作)其骄,分別對(duì)應(yīng)二狗:語(yǔ)文三汪:英語(yǔ)扯旷,一晌:數(shù)學(xué)三組數(shù)據(jù)拯爽。

  • 第一組數(shù)據(jù)(僅將被維護(hù)方對(duì)象添加進(jìn)維護(hù)方對(duì)象Set中,對(duì)維護(hù)方對(duì)象的單獨(dú)保存和刪除):由于操作對(duì)象是維護(hù)方钧忽,成功地在student毯炮、course以及中間表student_courses中分別添加了數(shù)據(jù)并成功進(jìn)行了刪除逼肯。若將刪除對(duì)象換成被維護(hù)方,同樣能夠成功刪除桃煎。
  • 第二組數(shù)據(jù)(僅將維護(hù)方對(duì)象添加進(jìn)被維護(hù)方對(duì)象Set中篮幢,對(duì)被維護(hù)方對(duì)象的單獨(dú)保存和刪除):操作對(duì)象在這里換成了被維護(hù)方。不負(fù)眾望为迈,出問(wèn)題了洲拇。保存的時(shí)候,student表和course表倒是都成功地插入了數(shù)據(jù)曲尸,但是中間表中赋续,并未產(chǎn)生對(duì)兩者數(shù)據(jù)的關(guān)聯(lián)。因此另患,在刪除的時(shí)候也只刪除了course中的數(shù)據(jù)纽乱。
  • 第三組數(shù)據(jù)( 將雙方對(duì)象均添加進(jìn)雙方Set中,對(duì)被維護(hù)方對(duì)象進(jìn)行保存和刪除):操作對(duì)象是被維護(hù)方昆箕,操作結(jié)果與第一組相同鸦列。

由此可知,實(shí)際操作中鹏倘,只要中間表建立了關(guān)聯(lián)薯嗤,即使是注解定義的被維護(hù)方也是可以對(duì)雙方關(guān)系進(jìn)行維護(hù)的。

一對(duì)多纤泵、多對(duì)一與一對(duì)一關(guān)系的介紹

當(dāng)我們了解完多對(duì)多關(guān)系以后骆姐,再來(lái)了解這三種關(guān)系映射就簡(jiǎn)單了許多。原理與多對(duì)多關(guān)系都是相同的捏题,下面將簡(jiǎn)要介紹其不同之處玻褪。

一對(duì)多關(guān)系與多對(duì)一關(guān)系
  • 一對(duì)多關(guān)系即數(shù)據(jù)庫(kù)中的一行數(shù)據(jù)關(guān)聯(lián)另一個(gè)數(shù)據(jù)庫(kù)中的多行關(guān)系。多對(duì)一與之相反公荧。
  • 一對(duì)多與多對(duì)一關(guān)系也可能會(huì)有中間表關(guān)聯(lián)兩者带射。但是我們一般不建議使用中間表。使用mapperBy可以避免系統(tǒng)生成中間表(會(huì)在多的一方數(shù)據(jù)庫(kù)中增加一個(gè)字段記錄外鍵)循狰。
  • 這兩個(gè)關(guān)系中的mappedBy一般聲明于一的一方窟社,即一的一方為被維護(hù)方。

聲明示例:

public class Student {
    @ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
    private ClassEntity classEntity;
    //其余略
}
public class ClassEntity {
    @OneToMany(cascade=CascadeType.PERSIST,fetch=FetchType.LAZY,mappedBy="classEntity")
    private Set<Student> students= new HashSet<>();
    //其余略
}
一對(duì)一關(guān)系
  • 一對(duì)一關(guān)系即兩個(gè)數(shù)據(jù)庫(kù)中的數(shù)據(jù)一一對(duì)應(yīng)绪钥。
    其他就沒(méi)有什么需要額外介紹的了灿里,原理與上面的是關(guān)系映射一樣的。
    聲明示例:
public class NewsResourceEntity{  
    @OneToOne(optional = false, cascade = CascadeType.MERGE)  
    private ResourceEntity resource;  
    //其余略
} 
public class ResourceEntity {  
    @OneToOne(optional = true, cascade = CascadeType.ALL, fetch=FetchType.LAZY, mappedBy = "resource")  
    private NewsResourceEntity newsResource;  
//其余略
} 

單向與雙向關(guān)聯(lián)的簡(jiǎn)介

本文從頭到尾所有的示例昧识,使用的都是雙向關(guān)聯(lián)钠四。即在關(guān)聯(lián)雙方都進(jìn)行關(guān)聯(lián)聲明盗扒。而事實(shí)上跪楞,除了雙向關(guān)聯(lián)缀去,還有一種用法是單向關(guān)聯(lián)。即在關(guān)聯(lián)的其中一方進(jìn)行關(guān)聯(lián)甸祭。
下面進(jìn)行介紹(轉(zhuǎn)):

當(dāng)使用單向關(guān)聯(lián)時(shí)缕碎,由父類管理關(guān)聯(lián)關(guān)系,子類無(wú)法管理池户,而這時(shí)咏雌,父親知道自己的兒子,但是校焦,從兒子對(duì)象不知道父親是誰(shuí)赊抖。
單向關(guān)聯(lián)時(shí),只指定<one-to-many>
當(dāng)使用雙向關(guān)聯(lián)時(shí)寨典,關(guān)聯(lián)關(guān)系的管理可以通過(guò)inverse指定氛雪,這時(shí),兒子能清楚的知道自己的父親是誰(shuí)耸成。 雙向關(guān)聯(lián)時(shí)报亩,還要指定<many-to-one>


以上。
希望我的文章對(duì)你能有所幫助井氢。
我不能保證文中所有說(shuō)法的百分百正確弦追,但我能保證它們都是我的理解和感悟以及拒絕復(fù)制黏貼。
有什么意見(jiàn)花竞、見(jiàn)解或疑惑劲件,歡迎留言討論。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末约急,一起剝皮案震驚了整個(gè)濱河市寇仓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌烤宙,老刑警劉巖遍烦,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異躺枕,居然都是意外死亡服猪,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)拐云,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)罢猪,“玉大人,你說(shuō)我怎么就攤上這事叉瘩∩排粒” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)危彩。 經(jīng)常有香客問(wèn)我攒磨,道長(zhǎng),這世上最難降的妖魔是什么汤徽? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任娩缰,我火速辦了婚禮,結(jié)果婚禮上谒府,老公的妹妹穿的比我還像新娘拼坎。我一直安慰自己,他們只是感情好完疫,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布泰鸡。 她就那樣靜靜地躺著,像睡著了一般壳鹤。 火紅的嫁衣襯著肌膚如雪鸟顺。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天器虾,我揣著相機(jī)與錄音讯嫂,去河邊找鬼。 笑死兆沙,一個(gè)胖子當(dāng)著我的面吹牛欧芽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播葛圃,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼千扔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了库正?” 一聲冷哼從身側(cè)響起曲楚,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎褥符,沒(méi)想到半個(gè)月后龙誊,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡喷楣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年趟大,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铣焊。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡逊朽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出曲伊,到底是詐尸還是另有隱情叽讳,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站岛蚤,受9級(jí)特大地震影響邑狸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜灭美,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一推溃、第九天 我趴在偏房一處隱蔽的房頂上張望昂利。 院中可真熱鬧届腐,春花似錦、人聲如沸蜂奸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)扩所。三九已至围详,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間祖屏,已是汗流浹背助赞。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留袁勺,地道東北人雹食。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像期丰,于是被迫代替她去往敵國(guó)和親群叶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容