第七色在线视频,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 是如何完成這個任務(wù)。

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

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é)的信息,注意,此時 student 對象處于持久化狀態(tài),意味著 student 對象在程序世界的行為可以同步到數(shù)據(jù)庫中。

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

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

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

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

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

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

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

圖片描述

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

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

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

圖片描述

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

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)系,以課程對象為主動方
				javaCourse.getStudents().remove(student);
				ccourse.getStudents().remove(student);			
				return null;
			}
		});

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

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

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

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

第二種解除方案

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

不管是哪種方案,切記,只能是由主動方提出分手。
行事之前,一定要先檢查數(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)映射中,有主動方和被動方一說。

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

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

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

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

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

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é)生對象維護。
可以進(jìn)入數(shù)據(jù)庫,查看一下:

圖片描述

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

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

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

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

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

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

很簡單??!

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

下面便是如你所想的實例代碼:

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;
	}
});

是的,這個代碼本身沒有問題。

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

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

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

否則就會有空指針異常拋出來。前面沒有,是因為數(shù)據(jù)庫中有數(shù)據(jù),Hibernate 幫咱們自動實例化了。

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

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

圖片描述

3. 級聯(lián)刪除

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

多對多關(guān)聯(lián)一對多關(guān)聯(lián)多了一張中間表,在進(jìn)行級聯(lián)刪除的時候,到底會發(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

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

他會說我好悲慘,才進(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é)生時,中間表中與此學(xué)生有關(guān)聯(lián)的信息,也就是說此學(xué)生選修的課程信息也自動被刪除了。

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

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

嘿嘿!現(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 課程的實例,誰讓 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;
			}
		});

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

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

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

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

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

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

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

4. 小結(jié)

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

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