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

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

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

1. 前言

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

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

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

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

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

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

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

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

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

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

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

創(chuàng)建班級類:

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

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

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

private ClassRoom classRoom;

除此之外,還需要讓 Hibernate 知道,這個對象屬性的值來自于班級表中的對應(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é)生是多的一邊,查詢班級表可以得到學(xué)生所在班級信息。
  • @JoinColumn 告訴 Hibernate 需要帶著指定的字段值到班級表中匹配數(shù)據(jù)。

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

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

為了讓事情變得簡單明了,在主配置文件中只保留學(xué)生類班級類的映射關(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é)課的模板對象跑一個空測試實例:

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

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

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

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

圖片描述

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

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é)生所在班級:"+stu.getClassRoom().getClassRoomName());
				return stu_;
			}
		});

控制臺輸出結(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é)生所在班級:c1911

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

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

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

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

private Set<Student> students;

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

為什么使用 Set 集合?

因為一個班級內(nèi)不可能出現(xiàn)兩個完全相同的學(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:很直白的說明了一個班級會有多名學(xué)生,指引 Hibernate 在填充數(shù)據(jù)時,要找到所有學(xué)生,別遺漏了;
  • 屬性 mappedBy=“classRoom”: 告訴 Hibernate,班級和學(xué)生之間的關(guān)系在學(xué)生類中已經(jīng)說的夠明白了,應(yīng)該不需要再廢話了吧。

OK!把前面的測試實例改為查詢班級信息:

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("班級名稱:"+classRoom.getClassRoomName());
System.out.println("------我是分隔線------------------------");
				System.out.println("班級學(xué)生人數(shù):"+classRoom.getStudents().size());
				return classRoom;
			}
		});

查看控制臺輸出結(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=?
班級名稱: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=?
班級學(xué)生人數(shù):2

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

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

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

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

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

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

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

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

  • FetchType.EAGER
  • FetchType.LAZY

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

是否采用延遲加載,由項目邏輯決定。

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

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

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

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

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

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

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

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

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

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

cascade 是一個枚舉類型:

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

測試刪除班級實例:

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)說明,因為有學(xué)生引用班級信息,班級信息是不能被刪除的。

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

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

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

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

測試實例:

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é)生被刪除了,班級也被刪除了!

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

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

圖片描述

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

圖片描述

測試刪除編號為 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,查看一下:
圖片描述

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

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

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

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

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

5. 小結(jié)

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

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

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