生活随笔
收集整理的這篇文章主要介紹了
【Hibernate步步为营】--(一对多映射)之双向关联
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
上篇文章討論了單向關聯的一對多映射,在一的一端維護雙向的關系這種做法雖然能實現但是存在很多缺陷,首先生成很多多余的SQL語句,因為多的一端不維護關系,只有一的一端維護,在進行操作時一的一端會發出多余的update語句;其次,因為多的一端不知道一的一端存在,所以在保存多的一端時如果外鍵為null值,并且在設計數據庫時關系字段設為非空,則將無法保存數據。因為單向關聯一對多存在很多缺點那就沒有其它的辦法了嗎,可以采用雙向關聯來優化。
一、一對多雙向關聯
? ? ? ? 這里繼續采用上篇文章的學生和班級作為示例,班級和學生之間是一對多的關系,一個班級中擁有多名學生,和上篇文章不同的是這里的關系是雙向的,也就是一的一端和多的一端同時維護關聯關系,所以它的對象圖如下:
? ? ? 對應的關系模型圖沒有太大的變化,因為它們之間的關系是雙向的,所以在關系模型中兩端同時維護關聯關系,映射到關系模型中如下圖所示:
? ? ? 在一對多的單向關聯中映射文件只需要在一的一端進行特殊配置就可以,使用<one-to-many>配置,并在對象模型中使用set迭代器來設置外聯的對象模型,但是不同的是在雙向的關聯中需要在多的一端添加對應的另一端的外鍵關聯,這時候就必須在多的一端使用<many-to-one>的關聯關系來標明這種雙向性。
? 1、映射
? ? ?這里還使用Classes和Student來做示例,在Classes一端的內容和上文相同不會發生變換,但是多的一端Student的配置會發生變化,也就是在映射文件中需要添加<many-to-one>標簽。
? ? ?Student.hbm.xml映射文件配置需要添加外鍵列<many-to-one>標簽,并且該列的名稱要和Classes.hbm.xml的外鍵列的名稱一致,具體如下代碼:
[html] view plain
copy <?xml?version="1.0"?>??<!DOCTYPE?hibernate-mapping?PUBLIC???????"-//Hibernate/Hibernate?Mapping?DTD?3.0//EN"??????"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">??<hibernate-mapping>??????<class?name="com.src.hibernate.Student"?table="t_student">??????????<id?name="id">??????????????<generator?class="native"/>??????????</id>??????????<property?name="name"/>????????????????????<many-to-one?name="classes"?column="classesid"></many-to-one>??????</class>??</hibernate-mapping>??
? ? ? ? Classes.hbm.xml映射文件的配置和上篇文章相同,需要注意的是在Classes.java文件中添加了set屬性映射對應了Student對象,所以在映射文件中需要添加set標簽來指示為對象模型中使用了set迭代器,具體配置如下代碼:
[html] view plain
copy <?xml?version="1.0"?>??<!DOCTYPE?hibernate-mapping?PUBLIC???????"-//Hibernate/Hibernate?Mapping?DTD?3.0//EN"??????"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">??<hibernate-mapping>??????<class?name="com.src.hibernate.Classes"?table="t_classes">??????????<id?name="id">??????????????<generator?class="native"/>??????????</id>??????????<property?name="name"/>??????????<set?name="students"?inverse="true">??????????????<key?column="classesid"></key>??????????????<one-to-many?class="com.src.hibernate.Student"></one-to-many>??????????</set>??????</class>??</hibernate-mapping>??
? ?2、類
? ? ? 映射文件的配置是直接對應著類來的,所以有了映射文件就能夠寫出相應的類,相同的有了類就能夠知道對應的映射文件如何編寫,那來看看相應的類代碼如何編寫。
? ? ?Student.java類,需要在類中添加關聯的班級對象屬性,在加載Student時能獲得Classes的相關信息。
[java] view plain
copy package?com.src.hibernate;????public?class?Student?{??????????????????private?Classes?classes;??????public?Classes?getClasses()?{??????????return?classes;??????}??????public?void?setClasses(Classes?classes)?{??????????this.classes?=?classes;??????}??????????????????private?int?id;??????public?int?getId()?{??????????return?id;??????}??????public?void?setId(int?id)?{??????????this.id?=?id;??????}??????????????????private?String?name;??????public?String?getName()?{??????????return?name;??????}??????public?void?setName(String?name)?{??????????this.name?=?name;??????}????????}??
? ? ? ? Classes.java文件具體代碼內容見上篇文章,這里就不在詳述。
? ? ? ? 有了對象模型接下來生成關系模型,生成的SQL語句如下:
[sql] view plain
copy alter?table?t_student?drop?foreign?key?FK4B907570FC588BF4??drop?table?if?exists?t_classes??drop?table?if?exists?t_student??create?table?t_classes?(id?integer?not?null?auto_increment,?name?varchar(255),?primary?key?(id))??create?table?t_student?(id?integer?not?null?auto_increment,?name?varchar(255),?classesid?integer,?primary?key?(id))??alter?table?t_student?add?index?FK4B907570FC588BF4?(classesid),?add?constraint?FK4B907570FC588BF4?foreign?key?(classesid)?references?t_classes?(id)??
?3、數據操作
? ? ? 建立表結構后來編寫測試方法來驗證數據的操作,首先來看看數據的插入,向表結構中插入數據,寫入數據時會有兩種情況,一種是首先創建一個Classes對象,并將對象寫入到數據庫中,然后創建Student對象,在Classes對象中添加學生對象;另外一種是先創建學生對象,并將學生對象寫入數據庫中,然后創建Classes對象將學生對象加入到Classes對象中,這兩種類型的操作最后是不相同的,來對比下。
? ?3.1 先寫班級后寫學生
? ? ? 先把班級寫入到數據庫中后,Classes對象進入了Transient狀態,并在數據庫中有了一行,這時再寫Student對象,Student對象會查找對應的Classes的主鍵將其寫入到表中,所以此時關系模型中的數據都是非空的,保存的代碼如下:
[java] view plain
copy public?void?testSave(){??????Session?session=null;??????try{????????????????????session=HibernateUtils.getSession();????????????????????session.beginTransaction();????????????????????Classes?classes=new?Classes();??????????classes.setName("class");??????????session.save(classes);????????????????????Student?student1=new?Student();??????????student1.setName("zhangsan");??????????student1.setClasses(classes);??????????session.save(student1);????????????????????Student?student2=new?Student();??????????student2.setName("lisi");??????????student2.setClasses(classes);??????????session.save(student2);????????????????????session.getTransaction().commit();??????}catch(Exception?e){??????????e.printStackTrace();??????????session.getTransaction().rollback();??????}finally{??????????HibernateUtils.closeSession(session);??????}??}??
? ? ?對應的寫入數據庫中的信息列表如下圖:
? ?3.2 先寫學生后寫班級
? ? ?先把學生寫入到數據庫中此時因為學生表需要獲取對應的班級列的主鍵信息,但是因為班級信息轉化到Transient狀態,所以在寫入學生信息時會有null值,代碼如下:
[java] view plain
copy public?void?testSave(){??????Session?session=null;??????try{????????????????????session=HibernateUtils.getSession();????????????????????session.beginTransaction();????????????????????Student?student1=new?Student();??????????student1.setName("zhangsan");??????????session.save(student1);????????????????????Student?student2=new?Student();??????????student2.setName("lisi");??????????session.save(student2);??????????????????????????????Classes?classes=new?Classes();??????????classes.setName("Classes");????????????????????Set?students=new?HashSet();??????????students.add(student1);??????????students.add(student2);????????????????????classes.setStudents(students);??????????????????????????????session.save(classes);??????????session.getTransaction().commit();??????}catch(Exception?e){??????????e.printStackTrace();??????????session.getTransaction().rollback();??????}finally{??????????HibernateUtils.closeSession(session);??????}??}??
? ? ?寫入后對應的數據庫視圖如下:
? ? ? ?對比兩種寫入操作,因為兩個寫入的先后順序不同所以出現了不同的結果,但因為是雙向的關聯關系所以在寫入時并不會發生異常。
? ??
? ?4、讀取操作
? ? ?相對于寫入數據而言,讀取數據就變得很簡單了,因為是雙向的關聯所以數據的讀取也是雙向的,可以從任何一端讀取另一端的信息,如下代碼:
[java] view plain
copy public?void?testLoad1(){??????Session?session=null;??????try{??????????session=HibernateUtils.getSession();??????????session.beginTransaction();??????????????????????????????Classes?classes=(Classes)session.load(Classes.class,1);??????????System.out.println("classes.name="+classes.getName());??????????Set?students=classes.getStudents();????????????????????for(Iterator?iter=students.iterator();iter.hasNext();){??????????????Student?student=(Student)iter.next();??????????????System.out.println("student.name="+student.getName());??????????}??????????????????????????????Student?stu=new?Student();??????????stu=(Student)session.load(Student.class,?1);??????????System.out.println("通過學生加載班級信息Classes.id=?"+stu.getClasses().getId());??????????session.getTransaction().commit();??????}catch(Exception?e){??????????e.printStackTrace();??????????session.getTransaction().rollback();??????}finally{??????????HibernateUtils.closeSession(session);??????}??}?? ? ? ? ?運行上面的測試語句,生成的對應的語句信息如下:
[html] view plain
copy Hibernate:?select?classes0_.id?as?id1_0_,?classes0_.name?as?name1_0_?from?t_classes?classes0_?where?classes0_.id=???classes.name=class??Hibernate:?select?students0_.classesid?as?classesid1_,?students0_.id?as?id1_,?students0_.id?as?id0_0_,?students0_.name?as?name0_0_,?students0_.classesid?as?classesid0_0_?from?t_student?students0_?where?students0_.classesid=???student.name=lisi??student.name=zhangsan??通過學生加載班級信息Classes.id=?1??
結語
雙向的一對多討論完成,如果在使用一對多關系時建議使用雙向的關聯關系,它可以優化關系的類型,而且也可以保證在寫入時不會出錯。總結兩篇文章,單向和雙向性其實是通過使用<one-to-many>和<many-to-one>來實現的,前者重在設置關聯關系,并不會生成新列,但是后者在生成關聯關系的同時會生成新列。
總結
以上是生活随笔為你收集整理的【Hibernate步步为营】--(一对多映射)之双向关联的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。