hibernate 一對多雙向關聯 詳解

1、解析:html

一、  一對多雙向關聯也就是說,在加載班級時,可以知道這個班級全部的學生。java

同時,在加載學生時,也可以知道這個學生所在的班級。mysql

二、咱們知道,一對多關聯映射和多對一關聯映射是同樣的,都是在多的一端加外鍵了。web

只不過是角度不一樣,咱們從Classes一端來看,它就是一對多,它維護了一個一指向多的關係。在加載班級時,可以把學生加載上來。返過來,多個學生是屬於一個班級的,它就是多對一。sql

三、而像咱們的用戶和組,就是多對一。多對一維護了一種知道,就是多指向一,因此在加載學生時,就能拿到這個學生所在的班級。若是可以拿到它所在的班級,那麼必須在對象模型Student中,持有Classes對象的一個引用。數據庫

 hibernate一對多關聯映射鈥斺斔潁ǚ淺3S茫

因此要在Student實體模型中,加入一個字段classes。這樣才能實現雙向的一對多的關聯映射。session

 

2、新建hibernate_one2many_2項目app

3、修改對象模型 :測試

一、在Student.class中加上classes字段。ui

package com.bjsxt.hibernate;

 

public class Student {

   private int id;

   private String name;

    Classesclasses;

   public Classes getClasses() {

      return classes;

    }

   public void setClasses(Classes classes) {

      this.classes = classes;

    }

   public int getId() {

      return id;

    }

   public void setId(int id) {

      this.id = id;

    }

   public String getName() {

      return name;

    }

   public void setName(String name) {

      this.name = name;

    }

 

}

二、Classes類的內容不變

package com.bjsxt.hibernate;

 

import java.util.Set;

 

public class Classes {

   private int id;

   private String name;

   private Set students;

   public Set getStudents() {

      return students;

    }

   public void setStudents(Set students) {

      this.students = students;

    }

   

   public int getId() {

      return id;

    }

   public void setId(int id) {

      this.id = id;

    }

   public String getName() {

      return name;

    }

   public void setName(String name) {

      this.name = name;

    }

 

}

 

這樣就實現雙向了,Student類中有Class對象的引用。

一樣,Classes類中有關Student對象的引用。

4、修映射文件。

一、修改改Student.hbm.xml映射文件

<?xmlversion="1.0"?>

<!DOCTYPE hibernate-mappingPUBLIC

   "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

   "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

   <classname="com.bjsxt.hibernate.Student" table="t_student">

      <idname="id">

         <!--主鍵的生成方式不能是uuid。由於這種生成方式是生成32位16進制的字符串

           而咱們的實體類中id的類型爲int.因此要修改主鍵的生成方式爲native.就是以數字形式自增-->

         <generatorclass="native"></generator>

     </id>

      <propertyname="name"/>

     

      <!--由於在Student端加了一個屬性,因此在Student的映射文件裏面要加上這個屬性的映射

       它的映射就是<many-to-one>

       name就是Student類中的屬性名classes

       若是隻指定屬性名,而沒有指定列,就會在咱們的表t_student中加入一個字段,名字是classes.

       而且這個字段也指向一的一端。

       但是在存儲時,classes這段是不會存的。

       緣由是t_classes表根本不知道這個字段的存在。

       由於在Classes.hbm.xml映射文件中,根本就沒有看到classes這個字段的影子。

       若是像咱們的用戶和組同樣,在咱們的多的一端存,那麼這個字段就能夠存了,由於在

       多的一端,也就是Student.hbm.xml文件中,配置了它。

       就如用戶和組,咱們new一個用戶,而後new一個組,而後把這個用戶設到這個組裏,這樣就存起來了。

       可是,若是在多的一端存,則classesid字段就存不上了。由於在Student.hbm.xml文件中,

       根本就沒有classesid這個字段的關係。因此也就不知道classesid的存在。

        

       因此,在映射時,映射的字段必須跟一對多那邊的名字如出一轍。咱們在<many-to-oen>標籤,用column屬性來指定

        

       因此說,在一對多雙向關聯映射中,在多的一端只有一個外鍵約束,就是跟在一的一端映射的列名相同的列名。

       也就是說,從多的一端存,與在一的一端存,都會存到這個字段classesid上。

        

       咱們說,many-to-one映射會加字段,但是在一對多雙向關聯映射中,這個關聯的字段已經有了,因此就不用再加了。

       -->

      <many-to-onename="classes"column="classesid"/>

        

   </class>

</hibernate-mapping>

二、  Classes.hbm.xml映射文件不變。

<?xmlversion="1.0"?>

<!DOCTYPE hibernate-mappingPUBLIC

   "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

   "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mappingpackage="com.bjsxt.hibernate">

   <class name="Classes" table="t_classes">

      <idname="id">

        

         <generatorclass="native"></generator>

     </id>

      <propertyname="name"/>

      <!--上面爲簡單屬性

       下面要看一下集合要如何映射

       答:集合要用set標籤來映射

      set標籤的名字屬性的值就是Classes類中的集合類型數據的變量名 -->

      <setname="students">

        <!-- 那麼要如何映射set裏面的東西呢?

          咱們知道ont2many的映射關係,與many2one的映射是同樣的,要在多的一端加字段的。

          可是到目前爲止,在t_student這張表裏面是沒有classid這個字段標識的。

          因此要把這個字段加過來,加過來以後,還要作爲外鍵指向t_classes這張表的主鍵

           咱們用key標籤來實現-->

           <!--在key標籤裏面要使用的屬性是列,就是給定在t_student表中的加上的列的名字。

          加了key這個標籤後,就會把classid這個字段加入到t_student這張表裏面了,

           它作爲外鍵指向t_class表的id字段。-->

        <keycolumn="classesid"></key>

        <!-- 接下來,採用one-to-many這個標籤,採用這個標籤,一方面是一對多,

          另外一方面,要用class這個屬性來指定Classes.java類中的students這個集合裏面

          究竟是什麼元素。咱們這個實例裏面是Student對象集合。

          必定要指定集合中的元素是什麼類型的對象。-->

        <one-to-manyclass="Student"/>

        <!--ont-to-many標籤就表明了students集合裏面是Student對象。

        這樣指明以後,classes就能找到student這張表。

        由於student在這裏都寫了。

        並且在Student.hbm.xml文件中,也已經映射Student對象映射到哪張表了。

        這樣就能夠把classid加到表t_student裏面,並且作爲外鍵指向t_classes表的主鍵id.-->

     </set>

   </class>

</hibernate-mapping>

5、數據庫就不用建立了,用一對多單向關聯那個數據庫。

6、表也不用導出了,就用一對多單向關聯那個數據庫的表了。

7、測試存儲

一、在多的一端存儲

解析:在Student中,已經持有了Classes,而且它們之間的關係也已經配置了。

因此咱們如今能夠new出classes來,而後調用student的setClasses()方法,將classes設進去就能夠了。

public void testSave3(){

      Session session=null;

      try{

          session=HibernateUtils.getSession();

          session.beginTransaction();

          

          Classes classes=new Classes();

          classes.setName("理想Database實驗室");

          //瞬時對象要保存。否則在後面的代碼中,保存Student時,會由於classs在數據庫中沒有而發生瞬時對象不存在異常

          session.save(classes);

          

          Student student1=new Student();

          student1.setName("玉兒");

          //給學生分配班級

          student1.setClasses(classes);

          session.save(student1);

          Student student2=new Student();

          student2.setName("黃根兒");

          student2.setClasses(classes);

          session.save(student2);

          

          

          session.getTransaction().commit();

      }catch(Exception e){

          e.printStackTrace();

          session.getTransaction().rollback();

      }finally{

          HibernateUtils.closeSession(session);

      }

    }

 

執行後在顯示SQL爲:

 

先保存班級

insert into t_classes (name) values (?)

 

再存學生,由於學生知道本身是哪班的。由於先創建的班級。爲何知道?由於先創建的關係。用student1.setClasses(classes)就知道這個學生所在的班級了,給它設上了,玉兒就是理想Database實驗室的。

insert into t_student (name, classesid) values (?, ?)

insert into t_student (name, classesid) values (?, ?)

 

8、總結:因此採用一對多關係映射時,都在多的一端來維護。

一、第七個步驟是先存班級,再存學生。那麼咱們像一對多單向關聯映射那樣,若是先存學生,再存班級,可不能夠?固然仍是能夠的。由於Class及Classes.hbm.xml文件沒有改變呀。

因此若是執行testSave2()方法:

 


 public void testSave2(){
  Session session=null;
  try{
   session=HibernateUtils.getSession();
   session.beginTransaction();
   
   //先創建學生對象集合
   Studentstudent1=new Student();
   student1.setName("菜10");
   session.save(student1);
   Studentstudent2=new Student();
   student2.setName("容祖兒");
   session.save(student2);
   
   //用泛型了
   //由於班級裏的學生是一個集合,因此要構造一個集合出來
   Set<Student> students=newHashSet<Student>();
   //向集合裏面加數據
   students.add(student1);
   students.add(student2);
     
   //要知道哪一個學生所在的班級,要new班級出來
   Classesclasses=new Classes();
   classes.setName("尚學堂");
   //這個班級裏面有哪些學生,要用set方法加上去。
   //這樣這個尚學堂班級裏面就有兩個學生了
   classes.setStudents(students);
   session.save(classes);
   
   session.getTransaction().commit();
  }catch(Exception e){
   e.printStackTrace();
   session.getTransaction().rollback();
  }finally{
   HibernateUtils.closeSession(session);
  }
 }
 

執行後,數據也可保存上去。數據庫中顯示存入後的數據

mysql> select * from t_classes;
+----+--------------------+
| id |name              |
+----+--------------------+
 1 |尚學堂            |
 2 | 理想Database實驗室 |
 3 |尚學堂            |
+----+--------------------+
3 rows in set (0.00 sec)

mysql> select * from t_student;
+----+--------+-----------+
| id | name   | classesid |
+----+--------+-----------+
 1 | 菜10  |        1 |
 2 | 容祖兒|        1 |
 3 | 玉兒  |        2 |
 4 | 黃根兒|        2 |
 5 | 菜10  |        3 |
 6 | 容祖兒|        3 |
+----+--------+-----------+
6 rows in set (0.00 sec)

 

二、因此若是想讓多的一端來維護,那麼本來由一的一端來維護的,要讓一的一端來維護的失消。

在Classes.hbm.xml文件中,默認反轉爲false。就是能夠在一的一端來維護的。在此<setname=」students」 inverse=」true」>

就是在set標籤中設置反轉爲true。就是反轉了,反給別人了,使一的一端失效。

三、這樣設置完以後,若是再執行testSave2(),就是先存學生,此時學生的classesid字段的值爲空。而後再存班級,而後由班級更新學生的classesid字段。這種反轉是關係的反轉。

執行後,在控制檯產生三條insert語句,以下:

insert into t_student (name,classesid) value(?,?)當存學生時,classesid字段值確定爲null.

insert into t_student (name,classesid) value(?,?)

insert into t_classes (name) value(?)

也就是說,t_classes表沒有發出update語句來更新t_student表的classesid字段的值。由於咱們已經設置爲反轉了,不讓一的一端來維護關係了,一的一端就無論了,因此update語句就不發了。雖然能夠保存,可是在一的一端來維護的關係失效了,也就是說,班級根本不知道有學生存在。

 

MYSqL數據庫顯示以下:

mysql> select * from t_classes;

+----+--------------------+

| id |name              |

+----+--------------------+

 1 |尚學堂            |

 2 | 理想Database實驗室 |

 3 |尚學堂            |

 4 |尚學堂            |

+----+--------------------+

5 rows in set (0.00 sec)

mysql> select * from t_student;

+----+--------+-----------+

| id | name   | classesid|

+----+--------+-----------+

 1 | 菜10  |        1 |

 2 | 容祖兒|        1 |

 3 | 玉兒  |        2|

 4 | 黃根兒|        2 |

 5 | 菜10  |        3 |

 6 | 容祖兒|        3 |

 7 | 菜10  |     NULL |

 8 | 容祖兒|     NULL |

+----+--------+-----------+

也就是說,執行結果以下:數據能夠正常保存,可是關係字段爲null.

 

9、因此當一的一端將關係設置爲反轉後,只能在多的一端來維護。

就是說,先new出classes,而後將其分配到學生裏面。

在多的一端維護的好處是,不用發出update。由於學生知道本身是哪一個班級的了。

並且,爲何在多的一端維護關係比較好?由於關係字段就在多的一端,就在student這個表裏。固然誰離的近,誰維護好了。

 

10、readme. txt

hibernate 一對多關聯映射雙向。(雙向關聯Classed<----->Student)

一對多雙向關聯映射:

   *在一一端的集合上使用<key>,在對方表中加入一個外鍵指向一一端

   *在多一端採用<many-to-one>

   

注意:<key>標籤指定的外鍵字段必須和<many-to-one>指定的外鍵字段一致,不然引用字段的錯誤

 

若是在「一」一端維護一對多關係, hibernate會發出多餘的update語句,因此咱們通常在多的一端來維護關聯關係

 

關於invserse屬性:

   inverse主要用在一對多和多對多雙向關聯上,inverse能夠被設置到集合標籤<set> 上,

   默認inverse爲false,因此咱們能夠從「一」一端和「多」一端維護關聯關係。

       若是設置成inverse爲true,則咱們只能從多一端來維護關聯關係

   

   注意:inverse屬性,隻影響數據的存儲,也就是持久化

   

 inverse和cascade

   *inverse是關聯關係的控制方向

    *cascade操做上的連鎖反應

 

十一

測試加載:加載學生,看一下能不能把學生對應的班級加載上來。

testLoad3()測試方法以下:


 public void testLoad3(){
  Session session=null;
  try{
   session=HibernateUtils.getSession();
   
   Studentstudent=(Student)session.load(Student.class,4);
   System.out.println(student.getName());
   System.out.println(student.getClasses().getName());
   session.beginTransaction();
   session.getTransaction().commit();
  }catch(Exception e){
   e.printStackTrace();
   session.getTransaction().rollback();
  }finally{
   HibernateUtils.closeSession(session);
  }
 }

 

 

生成的SQL語句:

select student0_.id as id1_0_, student0_.name as name1_0_,student0_.classesid as classesid1_0_ from t_student student0_ wherestudent0_.id=?

select classes0_.id as id0_0_, classes0_.name as name0_0_ fromt_classes classes0_ where classes0_.id=?

輸出的數據:

黃根兒

理想Database實驗室

相關文章
相關標籤/搜索