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

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

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

1. 前言

通過本節(jié)課程的學(xué)習(xí),你將發(fā)現(xiàn)關(guān)聯(lián)對象之間的微妙關(guān)系。相信這種關(guān)系對你更深入地認(rèn)識 HIbernate 有很大的幫助。

通過本節(jié)課程,你將了解到:

  • 多對多雙向關(guān)聯(lián)映射中哪一方是關(guān)系維系者;
  • 級聯(lián)操作與關(guān)系維系者。

2. 關(guān)系維系者

新學(xué)期開始了,同學(xué)們選擇了各自喜歡的課程,現(xiàn)在為學(xué)生添加選修課程的任務(wù)就要落在 Hibernate 的身上。一起來看看 Hibernate 是如何完成這個(gè)任務(wù)。

行動(dòng)之前,先假設(shè)一個(gè)需求:

Hibernate” 同學(xué)覺得自己選修的課程太難了,現(xiàn)在想重新選擇。

圖片描述

重新選修之前,先刪除原來選修的內(nèi)容:

執(zhí)行流程分析:

  1. 進(jìn)入學(xué)生表,查詢到 “Hibernate” 同學(xué)的信息;
  2. 刪除 “Hibernate” 同學(xué)的所有選修課程。

2.1 解除級聯(lián)對象之間的關(guān)系

刪除方式有兩種:

第一種解除方案

HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>();
		hibernateTemplate.template(new Notify<Student>() {
			@Override
			public Student action(Session session) {
				// 查詢學(xué)生
				Student student =(Student)session.get(Student.class, new Integer(1));		
				// 查詢Java課程
				Course javaCourse = (Course) session.get(Course.class, new Integer(1));
				// 查詢C課程
				Course ccourse = (Course) session.get(Course.class, new Integer(2));
				// 解除關(guān)系
				student.getCourses().remove(javaCourse);
				student.getCourses().remove(ccourse);
				return null;
			}
		});

查詢到 ”Hibernate“ 同學(xué)的信息,注意,此時(shí) student 對象處于持久化狀態(tài),意味著 student 對象在程序世界的行為可以同步到數(shù)據(jù)庫中。

查詢 ”Hibernate“ 同學(xué)選修的 JavaC 兩門課程,此時(shí)保存這兩個(gè)課程信息的對象也處于持久化狀態(tài)。

使用如下代碼解除學(xué)生和課程之間的關(guān)系:

student.getCourses().remove(javaCourse);
student.getCourses().remove(ccourse);

因?yàn)?strong>學(xué)生對象、課程對象都處于持久化狀態(tài)。它們在程序世界中的一言一行都會(huì)同步到數(shù)據(jù)庫中。

既然在程序世界解除了彼此之間的關(guān)系,在數(shù)據(jù)庫中,中間表中的關(guān)系描述數(shù)據(jù)也會(huì)自動(dòng)刪除。

從控制臺上所顯示出來的 SQL 語句其實(shí)也知道刪除已經(jīng)成功,這個(gè)就不貼出來了。

進(jìn)入 MySql 驗(yàn)證一下:

圖片描述

中間表中已經(jīng)不存在和 ”Hibernate“ 同學(xué)相關(guān)的課程信息。

但是,此時(shí)你可能會(huì)有一個(gè)想法,剛剛是以學(xué)生對象為主動(dòng)方,向課程對象提出了分手,那么,能不能以課程方為主動(dòng)方提出分手呢?

試一下便知,測試之前,先恢復(fù)原來的內(nèi)容:

圖片描述

執(zhí)行下面的實(shí)例代碼:

HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>();
		hibernateTemplate.template(new Notify<Student>() {
			@Override
			public Student action(Session session) {
				// Hiberante學(xué)生
				Student student =(Student)session.get(Student.class, new Integer(1));		
				// 查詢Java
				Course javaCourse = (Course) session.get(Course.class, new Integer(1));
				// 查詢C
				Course ccourse = (Course) session.get(Course.class, new Integer(2));
				// 解除關(guān)系,以課程對象為主動(dòng)方
				javaCourse.getStudents().remove(student);
				ccourse.getStudents().remove(student);			
				return null;
			}
		});

可能會(huì)讓你失望,這次操作對數(shù)據(jù)庫沒有任何影響。

可見,分手只能是由學(xué)生對象提出來。

雖然在前面課程中,咱們配置了學(xué)生類和課程類的雙向多對多關(guān)聯(lián)映射,但是,兩者之間只能有一個(gè)主動(dòng)方,這里要了解,所謂主動(dòng)方,就是關(guān)系的維系者。

關(guān)系的建立和解除只能由主動(dòng)方提供。是不是有點(diǎn)像霸道總裁的狗血故事。

第二種解除方案

Ok!一起繼續(xù)了解第 2 種方案。

不管是哪種方案,切記,只能是由主動(dòng)方提出分手。
行事之前,一定要先檢查數(shù)據(jù)是否存在。

HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>();
	hibernateTemplate.template(new Notify<Student>() {
		@Override
		public Student action(Session session) {
			// Hibernate學(xué)生
			Student student =(Student)session.get(Student.class, new Integer(1));		
           //解除關(guān)系
			student.getCourses().removeAll(student.getCourses());
			return null;
		}
	});

和第一種方案相比較,不再查詢課程信息,由學(xué)生對象單方面一次性解除關(guān)系。

student.getCourses().removeAll(student.getCourses());
				return null;

執(zhí)行結(jié)果沒有什么意外,程序世界中關(guān)系的解除操作同步到了數(shù)據(jù)庫中。

一起看看控制臺上輸出的信息:

Hibernate: 
    select
        student0_.stuId as stuId1_1_0_,
        student0_.stuName as stuName2_1_0_,
        student0_.stuPassword as stuPassw3_1_0_,
        student0_.stuPic as stuPic4_1_0_,
        student0_.stuSex as stuSex5_1_0_ 
    from
        Student student0_ 
    where
        student0_.stuId=?
Hibernate: 
    select
        courses0_.stuId as stuId1_1_1_,
        courses0_.courseId as courseId2_2_1_,
        course1_.courseId as courseId1_0_0_,
        course1_.courseDesc as courseDe2_0_0_,
        course1_.courseName as courseNa3_0_0_ 
    from
        score courses0_ 
    inner join
        Course course1_ 
            on courses0_.courseId=course1_.courseId 
    where
        courses0_.stuId=?
Hibernate: 
    delete 
    from
        score 
    where
        stuId=?

大家可以看到,Hibernate 接收到解除操作后,立即由中間表連接到課程表,把相關(guān)課程信息從中間表中抺出。

一切進(jìn)行得簡單而有序:

  • 記住,持久化對象的行為可以同步到數(shù)據(jù)庫中去;
  • 多對多雙向關(guān)聯(lián)映射中,有主動(dòng)方和被動(dòng)方一說。

2.2 建立級聯(lián)對象之間的關(guān)系

好!現(xiàn)在為 ”Hibernate“ 同學(xué)選修新的課程。比如說,想選擇 DB 課程和 JAVA 課程。

天呀,剛剛不是才解除了 JAVA 課程嗎。你管得著嗎,咱們就是這么任性,想解除就解除,想建立就建立 ,誰叫 Hibernate 這么方便呢。

有了前面的知識,應(yīng)該難不倒我們了。

現(xiàn)在來看看 Hibernate 實(shí)例:

HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>();
		hibernateTemplate.template(new Notify<Student>() {
			@Override
			public Student action(Session session) {
				// 添加新學(xué)生
				Student student =(Student)session.get(Student.class, new Integer(1));		
				// 查詢Java
				Course javaCourse = (Course) session.get(Course.class, new Integer(1));
				// 查詢C
				Course dbCourse = (Course) session.get(Course.class, new Integer(3));
				// 確定關(guān)系
				student.getCourses().add(javaCourse);
				student.getCourses().add(dbCourse);
				return null;
			}
		});

關(guān)系還是在程序中直接體現(xiàn),并且是由學(xué)生對象維護(hù)。
可以進(jìn)入數(shù)據(jù)庫,查看一下:

圖片描述

還是再叮囑一下,關(guān)系只能由學(xué)生方維護(hù)的。

此處,應(yīng)該有疑問,為什么只能是學(xué)生方,而不能是課程方,難道在 Java 的世界里也有命運(yùn)一說。

這個(gè)命運(yùn)的安排是由開發(fā)者來決定的,在進(jìn)行關(guān)聯(lián)注解時(shí),那一方使用了 mappedBy 屬性,則這一方就是被動(dòng)方,很好理解,這個(gè)屬性本身的含義就是說,聽對方的。

所以說,誰是霸道總裁,看開發(fā)者的心情。理論上講,多對多中,兩者應(yīng)該是平等關(guān)系。

剛剛的解除和重新建立都是對已經(jīng)存在的學(xué)生進(jìn)行的。

如果班里轉(zhuǎn)來了一名叫 ”HibernateTemplate“ 的新學(xué)生,他想選修 JAVADB,則應(yīng)該怎么操作呢?

很簡單??!

  • 學(xué)生沒有,添加就是;
  • 課程信息有,從表中查詢就是;
  • 關(guān)系不存在,建立就是。

下面便是如你所想的實(shí)例代碼:

HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>();
hibernateTemplate.template(new Notify<Student>() {
	@Override
	public Student action(Session session) {
		// 添加新學(xué)生
		Student student = new Student("HibernateTemplate", "男");
		// 查詢Java課程
		Course javaCourse = (Course) session.get(Course.class, new Integer(1));
		System.out.println(javaCourse.getCourseName());
	    // 查詢DB課程
		Course dbCourse = (Course) session.get(Course.class, new Integer(3));
		System.out.println(dbCourse.getCourseName());
	    // 確定關(guān)系
	    student.getCourses().add(javaCourse);
		student.getCourses().add(dbCourse);
		return null;
	}
});

是的,這個(gè)代碼本身沒有問題。

但是,這里會(huì)有一個(gè)小坑,這個(gè)坑沒有填的話,你可能會(huì)測試不成功。

學(xué)生類中,一定要記住對下面的屬性進(jìn)行實(shí)例化:

private Set<Course> courses=new HashSet<Course>();

否則就會(huì)有空指針異常拋出來。前面沒有,是因?yàn)閿?shù)據(jù)庫中有數(shù)據(jù),Hibernate 幫咱們自動(dòng)實(shí)例化了。

但現(xiàn)在是一個(gè)新學(xué)生,Hiberante 可不會(huì)幫你實(shí)例化他的課程集合屬性。

也就是不能任何時(shí)候都依靠 Hibernate,它也有顧全不到的地方。為了讓你寬心,還是看一下數(shù)據(jù)庫中數(shù)據(jù)吧:

圖片描述

3. 級聯(lián)刪除

前面講解雙向一對多的時(shí)候,也提到了級聯(lián)刪除。最大的印象就是,如果雙方都打開了級聯(lián)刪除,刪除時(shí)就如同推倒了多米諾骨牌的第一張牌,整個(gè)數(shù)據(jù)鏈都會(huì)刪除。

多對多關(guān)聯(lián)一對多關(guān)聯(lián)多了一張中間表,在進(jìn)行級聯(lián)刪除的時(shí)候,到底會(huì)發(fā)生什么事情?

在此也有必要拿出來說一說。

為了不讓事情的發(fā)展如山崩一樣不可控制,先打開學(xué)生類的級聯(lián)操作功能:

private Set<Course> courses=new HashSet<Course>();
	@ManyToMany(targetEntity = Course.class,cascade=CascadeType.ALL)
	@JoinTable(name = "score", joinColumns = @JoinColumn(name = "stuId", referencedColumnName = "stuId"), 
	inverseJoinColumns = @JoinColumn(name = "courseId", referencedColumnName = "courseId"))
	public Set<Course> getCourses() {
		return courses;
	}

這里使用 CascadeType.ALL。

來一段測試實(shí)例,刪除剛才添加的 HibernateTemplate 同學(xué)。

他會(huì)說我好悲慘,才進(jìn)來沒有多久。

HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>();
		hibernateTemplate.template(new Notify<Student>() {
			@Override
			public Student action(Session session) {
				// 查詢學(xué)生
				Student student =(Student)session.get(Student.class, new Integer(23));
				session.delete(student);
                return null;
			}
		});

無驚無喜,一切按照預(yù)先的設(shè)想進(jìn)行。
圖片描述

刪除學(xué)生時(shí),中間表中與此學(xué)生有關(guān)聯(lián)的信息,也就是說此學(xué)生選修的課程信息也自動(dòng)被刪除了。

但是,會(huì)有一個(gè)想法,如果刪除課程,則中間表中記錄的與此課程有關(guān)的信息是否會(huì)自動(dòng)刪除呢?

OK!開始行動(dòng)之前,可別忘記在課程類中打開級聯(lián)操作選項(xiàng):

嘿嘿!現(xiàn)在兩邊的級聯(lián)操作功能都已經(jīng)打開。

private Set<Student> students=new HashSet<Student>();
	@ManyToMany(targetEntity = Student.class, mappedBy = "courses",cascade=CascadeType.ALL)
	public Set<Student> getStudents() {
		return students;
	}

打開后,執(zhí)行刪除 C 課程的實(shí)例,誰讓 C 不好學(xué)了。

HibernateTemplate<Course> hibernateTemplate = new HibernateTemplate<Course>();
		hibernateTemplate.template(new Notify<Course>() {
			@Override
			public Course action(Session session) {
				// 查詢學(xué)生
				Course course =(Course)session.get(Course.class, new Integer(2));
				session.delete(course);
                
				return null;
			}
		});

這只是一個(gè)很簡單的代碼,但是卻發(fā)生如雪崩一樣的事件。

到底發(fā)生了什么事情?

大家進(jìn)入 MySql 看看就知道了。

圖片描述
3張表中空空如也,所有數(shù)據(jù)都沒有了。

就如同前面講解一對多的級聯(lián)刪除一樣。同樣適用于多對多關(guān)聯(lián)映射之中。

因兩邊都已經(jīng)打開了級聯(lián),刪除操作如同無法控制的壞情緒,刪除課程時(shí),以中間表為連接,反復(fù)來往于三張表,把相關(guān)信息全部刪除。

所以,使用級聯(lián)時(shí)一定要小心,否則,小心臟真的有點(diǎn)受不了。

4. 小結(jié)

好了,本節(jié)課可以結(jié)束了,通過本節(jié)課,大家了知道無論是雙向一對多,還是雙向多對多,都會(huì)有一個(gè)主動(dòng)方。
在雙向多對關(guān)聯(lián)中,主動(dòng)方有解除關(guān)系能力。

級聯(lián)操作功能用得好,能一勞永逸。但是,如果沒有用好,則會(huì)如同打開了潘多拉魔盒,發(fā)生雪崩一樣的災(zāi)難。