1、解析:html
一、 一對多雙向關聯也就是說,在加載班級時,可以知道這個班級全部的學生。java
同時,在加載學生時,也可以知道這個學生所在的班級。mysql
二、咱們知道,一對多關聯映射和多對一關聯映射是同樣的,都是在多的一端加外鍵了。web
只不過是角度不一樣,咱們從Classes一端來看,它就是一對多,它維護了一個一指向多的關係。在加載班級時,可以把學生加載上來。返過來,多個學生是屬於一個班級的,它就是多對一。sql
三、而像咱們的用戶和組,就是多對一。多對一維護了一種知道,就是多指向一,因此在加載學生時,就能拿到這個學生所在的班級。若是可以拿到它所在的班級,那麼必須在對象模型Student中,持有Classes對象的一個引用。數據庫
因此要在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實驗室