第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

首頁 慕課教程 Hibernate 入門教程 Hibernate 入門教程 Hibernate 一對多雙向關(guān)聯(lián)映射

Hibernate 一對多雙向關(guān)聯(lián)映射

1. 前言

本節(jié)課程和大家一起聊聊一對多關(guān)聯(lián)映射。通過本節(jié)課程,你將了解到:

  • 如何實(shí)現(xiàn)一對多關(guān)聯(lián)映射;

  • 如何實(shí)現(xiàn)雙向一對多關(guān)聯(lián)映射;

  • 關(guān)聯(lián)映射中的級(jí)聯(lián)操作。

2. 一對多關(guān)聯(lián)映射

關(guān)系型數(shù)據(jù)庫中表與表中的數(shù)據(jù)存在一對多(或多對一)關(guān)系。

如學(xué)生表、班級(jí)表。一個(gè)班級(jí)有多個(gè)學(xué)生,多個(gè)學(xué)生可以在同一個(gè)班級(jí)。

一對多或多對一本質(zhì)上是一樣的,如同一塊硬幣的正面和反面,只是看待事物的角度不同而已。

數(shù)據(jù)庫中有學(xué)生表、班級(jí)表。使用 Hibernate 進(jìn)行數(shù)據(jù)操作時(shí), 程序中就應(yīng)該有學(xué)生類、班級(jí)類。

同時(shí)學(xué)生類、班級(jí)類應(yīng)該使用 OOP 語法描述出如同學(xué)生表和班級(jí)表一樣的關(guān)系。并且還要讓 Hibernate 看得懂。

有了前面的基礎(chǔ),直接上代碼:

創(chuàng)建班級(jí)類:

@Entity
public class ClassRoom {
	private Integer classRoomId;
	private String classRoomName;
	private String classRoomDesc;
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	public Integer getClassRoomId() {
		return classRoomId;
	}

需求:查詢學(xué)生時(shí),得到學(xué)生所在班級(jí)信息。

進(jìn)入學(xué)生類,添加如下代碼,描述學(xué)生類班級(jí)類的關(guān)系:

private ClassRoom classRoom;

除此之外,還需要讓 Hibernate 知道,這個(gè)對象屬性的值來自于班級(jí)表中的對應(yīng)數(shù)據(jù),進(jìn)一步修改代碼:

private ClassRoom classRoom;
@ManyToOne(targetEntity=ClassRoom.class)
@JoinColumn(name="classRoomId")
public ClassRoom getClassRoom() {
	return classRoom;
}
  • @ManyToOne 告訴 Hibernate,從學(xué)生的角度來看,學(xué)生是多的一邊,查詢班級(jí)表可以得到學(xué)生所在班級(jí)信息。
  • @JoinColumn 告訴 Hibernate 需要帶著指定的字段值到班級(jí)表中匹配數(shù)據(jù)。

修改 Hibernate 主配置文件中內(nèi)容:

<property name="hbm2ddl.auto">create</property>
<mapping class="com.mk.po.Student" />
<mapping class="com.mk.po.ClassRoom" />

為了讓事情變得簡單明了,在主配置文件中只保留學(xué)生類班級(jí)類的映射關(guān)系。

學(xué)生類中的所有屬性描述:

private Integer stuId;
	private String stuName;
	private String stuSex;
	private String stuPassword;
	private Blob stuPic;
	private ClassRoom classRoom;
    @ManyToOne(targetEntity=ClassRoom.class)
    @JoinColumn(name="classRoomId")
	public ClassRoom getClassRoom() {
		return classRoom;
	}

使用上一節(jié)課的模板對象跑一個(gè)空測試實(shí)例:

@Test
public void testGetByTemplate() {
	HibernateTemplate<Student> hibernateTemplate=new HibernateTemplate<Student>();	
}

目的是讓 Hibernate 重新創(chuàng)建學(xué)生表、班級(jí)表。別忘記啦,自動(dòng)創(chuàng)建表后,修改回:

<property name="hbm2ddl.auto">update</property>

進(jìn)入 MySql,在學(xué)生表、班級(jí)表中手工添加幾條測試數(shù)據(jù):

圖片描述

到了完成需求的時(shí)候,測試下面實(shí)例:

HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>();
		hibernateTemplate.template(new Notify<Student>() {
			@Override
			public Student action(Session session) {
				Student stu=(Student)session.get(Student.class, new Integer(1));
				System.out.println("學(xué)生姓名:"+stu.getStuName());
				System.out.println("學(xué)生所在班級(jí):"+stu.getClassRoom().getClassRoomName());
				return stu_;
			}
		});

控制臺(tái)輸出結(jié)果:

Hibernate: 
    select
        student0_.stuId as stuId1_1_1_,
        student0_.classRoomId as classRoo6_1_1_,
        student0_.stuName as stuName2_1_1_,
        student0_.stuPassword as stuPassw3_1_1_,
        student0_.stuPic as stuPic4_1_1_,
        student0_.stuSex as stuSex5_1_1_,
        classroom1_.classRoomId as classRoo1_0_0_,
        classroom1_.classRoomDesc as classRoo2_0_0_,
        classroom1_.classRoomName as classRoo3_0_0_ 
    from
        Student student0_ 
    left outer join
        ClassRoom classroom1_ 
            on student0_.classRoomId=classroom1_.classRoomId 
    where
        student0_.stuId=?
學(xué)生姓名:Hibernate
學(xué)生所在班級(jí):c1911

Hibernate 使用 left outer join 構(gòu)建了一條多表查詢語句!

3. 雙向一對多關(guān)聯(lián)映射

需求:查詢班級(jí)時(shí),想知道班上有多少名學(xué)生,又應(yīng)該如何映射?

進(jìn)入班級(jí)類,添加如下屬性:

private Set<Student> students;

使用集合屬性 students,描述了一個(gè)班有多名學(xué)生。

為什么使用 Set 集合?

因?yàn)橐粋€(gè)班級(jí)內(nèi)不可能出現(xiàn)兩個(gè)完全相同的學(xué)生對象。

這還僅僅只是 OOP 層面上的關(guān)系,還需要告訴 Hibernate 應(yīng)該如何填充數(shù)據(jù)。

添加下面代碼:

private Set<Student> students;
@OneToMany(targetEntity=Student.class,mappedBy="classRoom")
public Set<Student> getStudents() {
	return students;
}
  • @OneToMany:很直白的說明了一個(gè)班級(jí)會(huì)有多名學(xué)生,指引 Hibernate 在填充數(shù)據(jù)時(shí),要找到所有學(xué)生,別遺漏了;
  • 屬性 mappedBy=“classRoom”: 告訴 Hibernate,班級(jí)和學(xué)生之間的關(guān)系在學(xué)生類中已經(jīng)說的夠明白了,應(yīng)該不需要再廢話了吧。

OK!把前面的測試實(shí)例改為查詢班級(jí)信息:

HibernateTemplate<ClassRoom> hibernateTemplate = new HibernateTemplate<ClassRoom>();
		 hibernateTemplate.template(new Notify<ClassRoom>() {
			@Override
			public ClassRoom action(Session session) {
				ClassRoom classRoom=(ClassRoom)session.get(ClassRoom.class, new Integer(1));
				System.out.println("班級(jí)名稱:"+classRoom.getClassRoomName());
System.out.println("------我是分隔線------------------------");
				System.out.println("班級(jí)學(xué)生人數(shù):"+classRoom.getStudents().size());
				return classRoom;
			}
		});

查看控制臺(tái)輸出結(jié)果:

Hibernate: 
    select
        classroom0_.classRoomId as classRoo1_0_0_,
        classroom0_.classRoomDesc as classRoo2_0_0_,
        classroom0_.classRoomName as classRoo3_0_0_ 
    from
        ClassRoom classroom0_ 
    where
        classroom0_.classRoomId=?
班級(jí)名稱:c1911
------我是分隔線------------------------
Hibernate: 
    select
        students0_.classRoomId as classRoo6_0_1_,
        students0_.stuId as stuId1_1_1_,
        students0_.stuId as stuId1_1_0_,
        students0_.classRoomId as classRoo6_1_0_,
        students0_.stuName as stuName2_1_0_,
        students0_.stuPassword as stuPassw3_1_0_,
        students0_.stuPic as stuPic4_1_0_,
        students0_.stuSex as stuSex5_1_0_ 
    from
        Student students0_ 
    where
        students0_.classRoomId=?
班級(jí)學(xué)生人數(shù):2

會(huì)發(fā)現(xiàn)一個(gè)很有意思的地方。Hibernate 查詢班級(jí)時(shí),構(gòu)建了兩條 SQL

先查詢班級(jí),當(dāng)需要學(xué)生信息時(shí),才構(gòu)建查詢學(xué)生的 SQL

大家應(yīng)該也猜出來了,當(dāng)從學(xué)生表查詢班級(jí)表時(shí),Hibernate 采用的是立即策略。

當(dāng)查詢從班級(jí)表查詢到學(xué)生表時(shí),Hibernate 采用的是延遲加載策略。

采用延遲加載都只有一個(gè)目的,需要時(shí)加載,提高響應(yīng)速度。

現(xiàn)在,學(xué)生類和班級(jí)類的映射配置信息,能讓 Hibernate 自動(dòng)從學(xué)生表查詢到班級(jí)表,也能從班級(jí)表查詢到學(xué)生表。

這種 2 個(gè)實(shí)體類中的映射關(guān)系就稱為雙向一對多關(guān)聯(lián)映射。

無論是 @ManyToOne 還是 @OneToMany 注解都有 fetch 屬性,可以設(shè)置的值有 2 個(gè)選擇:

  • FetchType.EAGER
  • FetchType.LAZY。

所以,在雙向一對多關(guān)聯(lián)映射可以選擇是否啟用延遲加載,這和一對一關(guān)聯(lián)映射中是一樣的,就不在此重復(fù)復(fù)述。

是否采用延遲加載,由項(xiàng)目邏輯決定。

4. 一對多關(guān)聯(lián)映射中的級(jí)聯(lián)操作

什么是級(jí)聯(lián)操作?

關(guān)系型數(shù)據(jù)庫中由主外鍵維系的兩張表,具有主從關(guān)系。

如學(xué)生表和班級(jí)表,班級(jí)班是主表,學(xué)生表是從表。

類似于刪除某一個(gè)班級(jí)的信息,則需要先刪除所在班的學(xué)生信息,再刪除班級(jí)信息,這個(gè)操作就是級(jí)聯(lián)操作。

所謂級(jí)聯(lián)操作,指操作一張表時(shí),是否會(huì)牽連到與之有關(guān)聯(lián)的其它表。

現(xiàn)在,咱們是使用 Hibernate 進(jìn)行數(shù)據(jù)操作,不可能還要?jiǎng)隈{自己親力親為吧。只需要做些簡單配置,就可以讓 Hibernate 自動(dòng)做級(jí)聯(lián)操作。

進(jìn)入班級(jí)類,修改代碼如下:

@OneToMany(targetEntity=Student.class,mappedBy="classRoom",cascade=CascadeType.REMOVE)
	public Set<Student> getStudents() {
		return students;
	}

很簡單,只需要使用 @OneToManycascade 屬性,就能讓 Hibernate 明白如何做級(jí)聯(lián)操作。默認(rèn)情況下,沒有級(jí)聯(lián)效應(yīng)。

cascade 是一個(gè)枚舉類型:

public enum CascadeType {
    ALL,
    PERSIST,
    MERGE,
    REMOVE,
	REFRESH,
    DETACH
}
  • ALL: 級(jí)聯(lián)所有操作;
  • PERSIST: 級(jí)聯(lián)新增;
  • MERGE: 級(jí)聯(lián)更新或者新增;
  • REMOVE: 級(jí)聯(lián)刪除;
  • REFRESH: 級(jí)聯(lián)刷新;
  • DETACH: 級(jí)聯(lián)分離。

測試刪除班級(jí)實(shí)例:

HibernateTemplate<ClassRoom> hibernateTemplate = new HibernateTemplate<ClassRoom>();
		 hibernateTemplate.template(new Notify<ClassRoom>() {
			@Override
			public ClassRoom action(Session session) {
				ClassRoom classRoom=(ClassRoom)session.get(ClassRoom.class, new Integer(1));	
				 session.delete(classRoom);
				return null;
			}
		});

如果不添加 cascade 相關(guān)說明,因?yàn)橛袑W(xué)生引用班級(jí)信息,班級(jí)信息是不能被刪除的。

添加后再測試,查看表中內(nèi)容:班級(jí)以及班級(jí)所在學(xué)生信息全部刪除!
圖片描述

刪除班級(jí)時(shí)能級(jí)聯(lián)刪除學(xué)生,反過來,刪除學(xué)生能刪除班級(jí)嗎?

想法很好,實(shí)踐是檢驗(yàn)真理的唯一手段,學(xué)生類中修改成如下代碼:

@ManyToOne(targetEntity=ClassRoom.class,cascade=CascadeType.REMOVE)
    @JoinColumn(name="classRoomId")
	public ClassRoom getClassRoom() {
		return classRoom;
	}

測試實(shí)例:

HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>();
		hibernateTemplate.template(new Notify<Student>() {
			@Override
			public Student action(Session session) {
				Student stu=(Student)session.get(Student.class, new Integer(2));
				session.delete(stu);
				return stu;
			}
		});

結(jié)果很殘酷!學(xué)生被刪除了,班級(jí)也被刪除了!

級(jí)聯(lián)級(jí)聯(lián),只要設(shè)置了級(jí)聯(lián),不管刪除學(xué)生還是班級(jí),只要在對應(yīng)表中有引用關(guān)系的數(shù)據(jù)就會(huì)被刪除。

現(xiàn)在,學(xué)生類、班級(jí)類中的級(jí)聯(lián)刪除都打開了。如果對下面情形的數(shù)據(jù)(編號(hào) 1、2 的學(xué)生的班級(jí)編號(hào)都為 1)進(jìn)行刪除操作,則會(huì)發(fā)生什么事情?

圖片描述

數(shù)據(jù)庫中的數(shù)據(jù)如下:

圖片描述

測試刪除編號(hào)為 1 的學(xué)生:

HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>();
		hibernateTemplate.template(new Notify<Student>() {
			@Override
			public Student action(Session session) {
				Student stu=(Student)session.get(Student.class, new Integer(1));
				session.delete(stu);
				return stu;
			}
		});

進(jìn)入 MySql,查看一下:
圖片描述

天呀!這是級(jí)聯(lián)還是株連呀,太讓人后怕,數(shù)據(jù)都沒有了。

刪除學(xué)生時(shí),會(huì)級(jí)聯(lián)刪除和學(xué)生有關(guān)的班級(jí),班級(jí)刪除時(shí),又會(huì)查看學(xué)生表中是否還存在與班級(jí)有關(guān)聯(lián)的學(xué)生,有,則一刀下去,連根拔起。

Hibernate 有點(diǎn)剎不住車,產(chǎn)生了級(jí)聯(lián)連鎖反應(yīng)。

針對上面的測試,如果班級(jí)表的級(jí)聯(lián)關(guān)閉,執(zhí)行測試代碼,請問結(jié)果又會(huì)怎樣?

本節(jié)課程,講解了級(jí)聯(lián)刪除,級(jí)聯(lián)添加的內(nèi)容留到下節(jié)課繼續(xù)展開。

5. 小結(jié)

本節(jié)課和大家聊了雙向一對多關(guān)聯(lián)映射。

無論是一對一雙向關(guān)聯(lián)映射,還是一對多雙向關(guān)聯(lián)映射。都可以根據(jù)需要隨時(shí)設(shè)置是否延遲加載、級(jí)聯(lián)等操作。

在使用級(jí)聯(lián)操作時(shí),一定要小心,避免產(chǎn)生連鎖反應(yīng),刪除了不應(yīng)該刪除的數(shù)據(jù)。