本文轉自:http://quicker.iteye.com/blog/657795
一、一對多單向關聯
多對一與一對多類似
一對多映射中,只需要在一方的POJO中加入Set.
在一方的映射中加入:
<set name="students">
<key column="class_id"></key>
<one-to-many class="Student"/>
</set>
起到指示作用,指示在多方的表中加入一個外鍵.
- Hibernate一對多單向關聯映射
- 這種映射的本質是利用了多對一的關聯映射的原理
- 多對一關聯映射:是在多的一端添加一個外鍵維護多指向一的關聯引用
- 一對多關聯映射:是在多的一端添加一個外鍵維護一指向多的關聯引用
- 也就是說,一對多和多對一的映射策略是一致的,只是站的角度不同
- 缺點:
- * 更新student表中的classesid字段時,需要對每一個student發出一個update的sql,
- 來更新classesid字段
- * 如果將t_student表中的classesis設置爲非空,則不能保存student數據,因爲關係是由
- classes維護的,在保存student時,還沒有對應的classesid被生成
具體示例如下:
- package com.lwf.hibernate.pojo;
- import java.util.HashSet;
- import java.util.Set;
- public class Classes {
- private int id;
- private String name;
- private Set students = new HashSet();
- 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;
- }
- public Set getStudents() {
- return students;
- }
- public void setStudents(Set students) {
- this.students = students;
- }
- }
- package com.lwf.hibernate.pojo;
- public class Student {
- private int id;
- private String name;
- private int age;
- 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;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- }
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 package="com.lwf.hibernate.pojo">
- <class name="Classes" table="t_classes">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <set name="students" >
- <key column="class_id" ></key>
- <one-to-many class="Student" />
- </set>
- </class>
- </hibernate-mapping>
Student.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 package="com.lwf.hibernate.pojo">
- <class name="Student" table="t_student">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <property name="age"/>
- </class>
- </hibernate-mapping>
具體的數據庫結構:
- mysql> desc student;
- +----------+--------------+------+-----+---------+----------------+
- | Field | Type | Null | Key | Default | Extra |
- +----------+--------------+------+-----+---------+----------------+
- | id | int(11) | NO | PRI | NULL | auto_increment |
- | name | varchar(255) | YES | | NULL | |
- | class_id | int(11) | YES | MUL | NULL | |
- +----------+--------------+------+-----+---------+----------------+
- 3 rows in set (0.06 sec)
- mysql> desc t_class;
- +-------+--------------+------+-----+---------+----------------+
- | Field | Type | Null | Key | Default | Extra |
- +-------+--------------+------+-----+---------+----------------+
- | id | int(11) | NO | PRI | NULL | auto_increment |
- | name | varchar(255) | YES | | NULL | |
- +-------+--------------+------+-----+---------+----------------+
測試方法:
- package com.lwf.hibernate.test;
- import java.util.HashSet;
- import java.util.Iterator;
- import java.util.Set;
- import junit.framework.TestCase;
- import org.hibernate.Session;
- import com.lwf.hibernate.pojo.Classes;
- import com.lwf.hibernate.pojo.Student;
- import com.lwf.hibernate.util.HibernateUtil;
- public class One2Many_Test extends TestCase{
- //從classes這一端來維護,即在單方維護,會產生insert和update語句.
- public void testClasses(){
- Session session = HibernateUtil.getSession();
- session.beginTransaction();
- try{
- Set students = new HashSet();
- for(int i=0;i<5;i++){
- Student s = new Student();
- s.setName("name" + i);
- s.setAge((10 + i));
- students.add(s);
- session.save(s);
- }
- Classes cla = new Classes();
- cla.setName("class1");
- cla.setStudents(students);
- session.save(cla);
- //先保存,最後根據class_id更新Student
- HibernateUtil.commit(session);
- }catch(Exception e){
- HibernateUtil.roolback(session);
- }finally{
- HibernateUtil.closeSession(session);
- }
- }
- public void testLoad(){
- Session session = HibernateUtil.getSession();
- session.beginTransaction();
- try{
- Classes s = (Classes)session.load(Classes.class, 1);
- Set student = s.getStudents();
- for (Iterator iterator = student.iterator(); iterator.hasNext();) {
- Student stu = (Student) iterator.next();
- System.out.println(stu.getName());
- }
- HibernateUtil.commit(session);
- }catch(Exception e){
- HibernateUtil.roolback(session);
- }finally{
- HibernateUtil.closeSession(session);
- }
- }
- }
注意保存生成的語句:
- Hibernate: insert into t_student (name, age) values (?, ?)
- Hibernate: insert into t_student (name, age) values (?, ?)
- Hibernate: insert into t_student (name, age) values (?, ?)
- Hibernate: insert into t_student (name, age) values (?, ?)
- Hibernate: insert into t_student (name, age) values (?, ?)
- Hibernate: insert into t_classes (name) values (?)
- Hibernate: update t_student set class_id=? where id=?
- Hibernate: update t_student set class_id=? where id=?
- Hibernate: update t_student set class_id=? where id=?
- Hibernate: update t_student set class_id=? where id=?
- Hibernate: update t_student set class_id=? where id=?
先保存student,再保存classes最後根據classes的id更新student的class_id
這實際上是由classes這方來維護兩者的關聯關係.
正常情況下關係應該由多的一方來維護,在雙向關聯中我們從多方來維護兩者的關係.這樣就可以避免update語句的出現.而是直接insert即可.
二、一對多雙向關聯
下面我們看看雙向的一對多映射,實際上就是把一對多與多對一結合起來看.
- Hibernate 一對多雙向關聯映射
- 一對多雙向關聯映射的方法:
- 在一一端:
- 在集合標籤裏面使用<key>標籤來表明需要在對方的表中添加一個外鍵指向一一端。
- 在多一端:
- 使用<many-to-one>標籤來映射。
- 需要注意:<key>標籤所指定的外鍵字段名需要與<many-to-one>標籤定義的外鍵字段名一致,否則便會造成引用數據的
- 丟失!
- --------------------------------------------------------------------------------------
- 如果從一端來維護一對多雙向關聯的關係,hibernate會發出多餘的update語句,所以
- 一般地情況下,我們便會從多一端來維護其關聯關係!
- ----------------------------------------------------
在單向映射的基礎上加上多對一關聯即可.更改的文件:
- package com.lwf.hibernate.pojo;
- public class Student {
- private int id;
- private String name;
- private int age;
- private 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;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- public Classes getClasses() {
- return classes;
- }
- public void setClasses(Classes classes) {
- this.classes = classes;
- }
- }
Student.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 package="com.lwf.hibernate.pojo">
- <class name="Student" table="t_student">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <property name="age"/>
- <many-to-one name="classes" column="class_id"></many-to-one>
- </class>
- </hibernate-mapping>
即從Student的角度就是多對一,而從Classes角度就是一對多.
此時的表結構與單向的時候是一樣的.
- package com.lwf.hibernate.test;
- import java.util.HashSet;
- import java.util.Iterator;
- import java.util.Set;
- import junit.framework.TestCase;
- import org.hibernate.Session;
- import com.lwf.hibernate.pojo.Classes;
- import com.lwf.hibernate.pojo.Student;
- import com.lwf.hibernate.util.HibernateUtil;
- public class One2Many_Test extends TestCase{
- //從classes這一端來維護,即在單方維護,會產生insert和update語句.
- public void testClasses(){
- Session session = HibernateUtil.getSession();
- session.beginTransaction();
- try{
- Set students = new HashSet();
- for(int i=0;i<5;i++){
- Student s = new Student();
- s.setName("name" + i);
- s.setAge((10 + i));
- students.add(s);
- session.save(s);
- }
- Classes cla = new Classes();
- cla.setName("class1");
- cla.setStudents(students);
- session.save(cla);
- //先保存,最後根據class_id更新Student
- HibernateUtil.commit(session);
- }catch(Exception e){
- HibernateUtil.roolback(session);
- }finally{
- HibernateUtil.closeSession(session);
- }
- }
- //由於配置了雙向關聯,所以下面先保存了classes再保存student只有insert語句,沒有update語句
- public void testStudent(){
- Session session = HibernateUtil.getSession();
- session.beginTransaction();
- try{
- Classes cla = new Classes();
- cla.setName("class111");
- session.save(cla);
- for (int i = 0; i < 5; i++) {
- Student s = new Student();
- s.setName("name111"+i);
- s.setClasses(cla);
- session.save(s);
- }
- HibernateUtil.commit(session);
- }catch(Exception e){
- HibernateUtil.roolback(session);
- }finally{
- HibernateUtil.closeSession(session);
- }
- }
- //從classes得到student,主要測試單向關聯
- public void testLoadClass(){
- Session session = HibernateUtil.getSession();
- session.beginTransaction();
- try{
- Classes s = (Classes)session.load(Classes.class, 1);
- Set student = s.getStudents();
- for (Iterator iterator = student.iterator(); iterator.hasNext();) {
- Student stu = (Student) iterator.next();
- System.out.println(stu.getName());
- }
- HibernateUtil.commit(session);
- }catch(Exception e){
- HibernateUtil.roolback(session);
- }finally{
- HibernateUtil.closeSession(session);
- }
- }
- //從student得到classes,配置了雙向關聯後的測試
- public void testLoadStudent(){
- Session session = HibernateUtil.getSession();
- session.beginTransaction();
- try{
- Student s = (Student)session.load(Student.class, 1);
- System.out.println(s.getName());
- System.out.println(s.getClasses().getName());
- HibernateUtil.commit(session);
- }catch(Exception e){
- HibernateUtil.roolback(session);
- }finally{
- HibernateUtil.closeSession(session);
- }
- }
- }
主要區別:
由於配置了雙向關聯,所以下面先保存了classes再保存student只有insert語句,沒有update語句
以下是testStudent方法產生的SQL語句
- Hibernate: insert into t_classes (name) values (?)
- Hibernate: insert into t_student (name, age, class_id) values (?, ?, ?)
- Hibernate: insert into t_student (name, age, class_id) values (?, ?, ?)
- Hibernate: insert into t_student (name, age, class_id) values (?, ?, ?)
- Hibernate: insert into t_student (name, age, class_id) values (?, ?, ?)
- Hibernate: insert into t_student (name, age, class_id) values (?, ?, ?)
三、下面討論一下關於INVERSE的用法:
- 關於inverse屬性:
- inverse屬性可以被設置到集合標籤<set>上,表示在存儲雙向一對多關聯映射的時候,
- 存儲的是那一方的關聯引用。默認情況下,inverse=“false”,所以,我們可以從一一端
- 或者多一端來維護兩者之間的關係;如果我們設置inverse=“true”,則只能通過多一端來
- 維護兩者之間的關係。inverse屬性可以被用在一對多和多對多雙向關聯中;
- 注意:inverse屬性只是在將數據持久化到數據庫的過程中發揮作用.
主要看看上面雙向關聯中testClasses方法,當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 package="com.lwf.hibernate.pojo">
- <class name="Classes" table="t_classes">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <set name="students" >
- <key column="class_id" ></key>
- <one-to-many class="Student" />
- </set>
- </class>
- </hibernate-mapping>
我們知道上面的testClasses方法產生的sql語句是先insert再update,上面已經講到是從一方來維護關聯關係的.那麼現在我們把classes.hbm.xml的set上面增加inverse="true"即
- <set name="students" inverse="true">
- <key column="class_id" ></key>
- <one-to-many class="Student" />
- </set>
那麼這時候testClasses方法產生的SQL語句是?
- Hibernate: insert into t_student (name, age, class_id) values (?, ?, ?)
- Hibernate: insert into t_student (name, age, class_id) values (?, ?, ?)
- Hibernate: insert into t_student (name, age, class_id) values (?, ?, ?)
- Hibernate: insert into t_student (name, age, class_id) values (?, ?, ?)
- Hibernate: insert into t_student (name, age, class_id) values (?, ?, ?)
- Hibernate: insert into t_classes (name) values (?)
雖然只產生了insert語句,但因爲t_classes插入語句在後,所以數據庫裏面對應的t_student的記錄:
- mysql> select * from t_student;
- +----+-------+------+----------+
- | id | name | age | class_id |
- +----+-------+------+----------+
- | 1 | name0 | 10 | NULL |
- | 2 | name1 | 11 | NULL |
- | 3 | name2 | 12 | NULL |
- | 4 | name3 | 13 | NULL |
- | 5 | name4 | 14 | NULL |
- +----+-------+------+----------+
顯然class_id爲空.沒有更新.
即用了INVERSE,將強制要求從多的一方來維護關係.即當設置了INVERSE屬性後,要求使用testStudent方法來做保存.
而INVERSE屬性沒有設置時,對於雙向的一對多關聯,我們即可以使用testClasses又可以使用testStudent方法進行保存,但是他們的區別是testClasses操作中有insert和update語句,而testStudent只有insert語句.顯然使用testStudent即從多方來維護關聯關係節約了資源...