【Hibernate步步為營】--(一對多映射)之雙向關聯

來源:互聯網
上載者:User

標籤:style   class   blog   c   code   java   

       上篇文章討論了單向關聯的一對多映射,在一的一端維護雙向的關係這種做法雖然能實現但是存在很多缺陷,首先產生很多多餘的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的外鍵列的名稱一致,具體如下代碼:

<?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"/><!-- 在多的一端Student中添加一行新的Classes列 ,並且列的名稱要和Classes.hbm.xml的列明相同--><many-to-one name="classes" column="classesid"></many-to-one></class></hibernate-mapping>

        Classes.hbm.xml對應檔的配置和上篇文章相同,需要注意的是在Classes.java檔案中添加了set屬性對應對應了Student對象,所以在對應檔中需要添加set標籤來指示為物件模型中使用了set迭代器,具體配置如下代碼:

<?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的相關資訊。

package com.src.hibernate;public class Student {//關聯的班級對象private Classes classes;public Classes getClasses() {return classes;}public void setClasses(Classes classes) {this.classes = classes;}//學生idprivate 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語句如下:

alter table t_student drop foreign key FK4B907570FC588BF4drop table if exists t_classesdrop table if exists t_studentcreate 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的主鍵將其寫入到表中,所以此時關聯式模式中的資料都是非空的,儲存的代碼如下:

public void testSave(){Session session=null;try{//建立session對象session=HibernateUtils.getSession();//開啟事務session.beginTransaction();//建立班級對象,將班級對象寫入到資料庫中Classes classes=new Classes();classes.setName("class");session.save(classes);//建立學生1對象,將學生對象寫入到資料庫中Student student1=new Student();student1.setName("zhangsan");student1.setClasses(classes);session.save(student1);//建立學生2對象,將學生對象寫入到資料庫中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值,代碼如下:

public void testSave(){Session session=null;try{//建立session對象session=HibernateUtils.getSession();//開啟事務session.beginTransaction();//建立學生1對象,將學生對象寫入到資料庫中Student student1=new Student();student1.setName("zhangsan");session.save(student1);//建立學生2對象,將學生對象寫入到資料庫中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中classes.setStudents(students);//可以成功儲存資料//但是會發出多餘的update語句來維持關係,因為是一對多的原因session.save(classes);session.getTransaction().commit();}catch(Exception e){e.printStackTrace();session.getTransaction().rollback();}finally{HibernateUtils.closeSession(session);}}

     寫入後對應的資料庫檢視如下:


       對比兩種寫入操作,因為兩個寫入的先後順序不同所以出現了不同的結果,但因為是雙向的關聯關係所以在寫入時並不會發生異常。

    

   4、讀取操作
     相對於寫入資料而言,讀取資料就變得很簡單了,因為是雙向的關聯所以資料的讀取也是雙向的,可以從任何一端讀取另一端的資訊,如下代碼:
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);}}

       運行上面的測試語句,產生的對應的語句資訊如下:
Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_ from t_classes classes0_ where classes0_.id=?classes.name=classHibernate: 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=lisistudent.name=zhangsan通過學生載入班級資訊Classes.id= 1


結語
        雙向的一對多討論完成,如果在使用一對多關聯性時建議使用雙向的關聯關係,它可以最佳化關係的類型,而且也可以保證在寫入時不會出錯。總結兩篇文章,單向和雙向性其實是通過使用<one-to-many>和<many-to-one>來實現的,前者重在設定關聯關係,並不會產生新列,但是後者在產生關聯關係的同時會產生新列。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.