hibernate 多對多(many-to-many)

多對多(many-to-many):在操作和性能方面都不太理想,所以多對多的映射使用較少,實際使用中最好轉換成一對多的對象模型;hibernate會爲我們創建中間關聯表,轉換成兩個一對多。

1. E-R圖


 

2. 實體類:

Teacher實體類如下:

Java代碼 複製代碼 收藏代碼
  1. package com.reiyen.hibernate.domain;   
  2.   
  3. import java.util.Set;   
  4.   
  5. public class Teacher {   
  6.   
  7.     private int id;   
  8.     private String name;   
  9.     private Set<Student> students;   
  10.        //setter和getter方法   
  11.   
  12. }  
package com.reiyen.hibernate.domain;

import java.util.Set;

public class Teacher {

	private int id;
	private String name;
	private Set<Student> students;
       //setter和getter方法

}

 Student實體類如下:

Java代碼 複製代碼 收藏代碼
  1. package com.reiyen.hibernate.domain;   
  2.   
  3. import java.util.Set;   
  4.   
  5. public class Student {   
  6.   
  7.     private int id;   
  8.     private String name;   
  9.     private Set<Teacher> teachers;   
  10.         //setter和getter方法   
  11.   
  12. }  
package com.reiyen.hibernate.domain;

import java.util.Set;

public class Student {

	private int id;
	private String name;
	private Set<Teacher> teachers;
        //setter和getter方法

}

 3.映射文件如下:

Teacher.hbm.xml如下:

Xml代碼 複製代碼 收藏代碼
  1. <?xml version="1.0"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC    
  3.     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"   
  4.     "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
  5. <hibernate-mapping package="com.reiyen.hibernate.domain">  
  6.     <class name="Teacher">  
  7.         <id name="id">  
  8.             <generator class="native" />  
  9.         </id>  
  10.         <property name="name" />  
  11.         <!-- 通過table項告訴hibernate中間表的名稱 -->  
  12.         <set name="students" table="teacher_student">  
  13.             <!-- 通過key屬性告訴hibernate在中間表裏面查詢teacher_id值相應的teacher記錄 -->  
  14.             <key column="teacher_id" />  
  15.             <!-- 通過column項告訴hibernate對student表中查找student_id值相就的studnet記錄 -->  
  16.             <many-to-many class="Student" column="student_id" />  
  17.         </set>  
  18.     </class>  
  19. </hibernate-mapping>  
<?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.reiyen.hibernate.domain">
	<class name="Teacher">
		<id name="id">
			<generator class="native" />
		</id>
		<property name="name" />
		<!-- 通過table項告訴hibernate中間表的名稱 -->
		<set name="students" table="teacher_student">
			<!-- 通過key屬性告訴hibernate在中間表裏面查詢teacher_id值相應的teacher記錄 -->
			<key column="teacher_id" />
			<!-- 通過column項告訴hibernate對student表中查找student_id值相就的studnet記錄 -->
			<many-to-many class="Student" column="student_id" />
		</set>
	</class>
</hibernate-mapping>

 Student.hbm.xml如下:

Xml代碼 複製代碼 收藏代碼
  1. <?xml version="1.0"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC    
  3.     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"   
  4.     "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
  5. <hibernate-mapping package="com.reiyen.hibernate.domain">  
  6.     <class name="Student" >  
  7.         <id name="id" >  
  8.             <generator class="native" />  
  9.         </id>  
  10.         <property name="name" />  
  11.         <set name="teachers" table="teacher_student">  
  12.         <key column="student_id" />  
  13.         <many-to-many class="Teacher" column="teacher_id"/>  
  14.         </set>  
  15.     </class>  
  16. </hibernate-mapping>  
<?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.reiyen.hibernate.domain">
	<class name="Student" >
		<id name="id" >
			<generator class="native" />
		</id>
		<property name="name" />
		<set name="teachers" table="teacher_student">
		<key column="student_id" />
		<many-to-many class="Teacher" column="teacher_id"/>
		</set>
	</class>
</hibernate-mapping>

 一定要注意映射文件中<many-to-many class="Teacher" column="teacher_id"/>中class的值,它必須與你另一個關聯映射文件中的class屬性的name值一致,其實就是與你的實體類的類名一致,如:<many-to-many class="Teacher" column="teacher_id"/>中class的值就不能寫成"teacher"。如果寫成這樣的話,就會拋出如下異常:An association from the table teacher_student refers to an unmapped class: com.reiyen .hibernate.domain.teacher

4. 測試程序如下:

Java代碼 複製代碼 收藏代碼
  1. public class Many2Many {   
  2.     public static void main(String[] args) {   
  3.         add();   
  4.         //query(1);   
  5.     }   
  6.   
  7.     static void query(int id) {   
  8.         Session s = null;   
  9.         Transaction tx = null;   
  10.         try {   
  11.             s = HibernateUtil.getSession();   
  12.             tx = s.beginTransaction();   
  13.             Teacher t = (Teacher) s.get(Teacher.class, id);   
  14.             System.out.println("students:" + t.getStudents().size());   
  15.             tx.commit();   
  16.         } finally {   
  17.             if (s != null)   
  18.                 s.close();   
  19.         }   
  20.     }   
  21.   
  22.     static void add() {   
  23.         Session s = null;   
  24.         Transaction tx = null;   
  25.         try {   
  26.             Set<Teacher> ts = new HashSet<Teacher>();   
  27.   
  28.             Teacher t1 = new Teacher();   
  29.             t1.setName("t1 name");   
  30.             ts.add(t1);   
  31.   
  32.             Teacher t2 = new Teacher();   
  33.             t2.setName("t2 name");   
  34.             ts.add(t2);   
  35.   
  36.             Set<Student> ss = new HashSet<Student>();   
  37.             Student s1 = new Student();   
  38.             s1.setName("s1");   
  39.             ss.add(s1);   
  40.   
  41.             Student s2 = new Student();   
  42.             s2.setName("s2");   
  43.             ss.add(s2);   
  44.   
  45.             t1.setStudents(ss);  //1   
  46.             t2.setStudents(ss);  //1   
  47. //             
  48. //          s1.setTeachers(ts);  //2   
  49. //          s2.setTeachers(ts);  //2   
  50.   
  51.             s = HibernateUtil.getSession();   
  52.             tx = s.beginTransaction();   
  53.             s.save(t1);   
  54.             s.save(t2);   
  55.             s.save(s1);   
  56.             s.save(s2);   
  57.             tx.commit();   
  58.         } finally {   
  59.             if (s != null)   
  60.                 s.close();   
  61.         }   
  62.     }   
  63. }  
public class Many2Many {
	public static void main(String[] args) {
		add();
		//query(1);
	}

	static void query(int id) {
		Session s = null;
		Transaction tx = null;
		try {
			s = HibernateUtil.getSession();
			tx = s.beginTransaction();
			Teacher t = (Teacher) s.get(Teacher.class, id);
			System.out.println("students:" + t.getStudents().size());
			tx.commit();
		} finally {
			if (s != null)
				s.close();
		}
	}

	static void add() {
		Session s = null;
		Transaction tx = null;
		try {
			Set<Teacher> ts = new HashSet<Teacher>();

			Teacher t1 = new Teacher();
			t1.setName("t1 name");
			ts.add(t1);

			Teacher t2 = new Teacher();
			t2.setName("t2 name");
			ts.add(t2);

			Set<Student> ss = new HashSet<Student>();
			Student s1 = new Student();
			s1.setName("s1");
			ss.add(s1);

			Student s2 = new Student();
			s2.setName("s2");
			ss.add(s2);

			t1.setStudents(ss);  //1
			t2.setStudents(ss);  //1
//			
//			s1.setTeachers(ts);  //2
//			s2.setTeachers(ts);  //2

			s = HibernateUtil.getSession();
			tx = s.beginTransaction();
			s.save(t1);
			s.save(t2);
			s.save(s1);
			s.save(s2);
			tx.commit();
		} finally {
			if (s != null)
				s.close();
		}
	}
}

 運行此程序後:控制檯打印的sql語句如下所示:

Hibernate: insert into Teacher (name) values (?)
Hibernate: insert into Teacher (name) values (?)
Hibernate: insert into Student (name) values (?)
Hibernate: insert into Student (name) values (?)
Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)
Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)
Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)
Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)

一共在中間表裏面插入了4條記錄。

中間表結構如下所示:

DROP TABLE IF EXISTS `test`.`teacher_student`;
CREATE TABLE  `test`.`teacher_student` (
  `teacher_id` int(11) NOT NULL,
  `student_id` int(11) NOT NULL,
  PRIMARY KEY (`student_id`,`teacher_id`),
  KEY `FK2E2EF2DE6C8A2663` (`teacher_id`),
  KEY `FK2E2EF2DE5BEEDBC3` (`student_id`),
  CONSTRAINT `FK2E2EF2DE5BEEDBC3` FOREIGN KEY (`student_id`) REFERENCES `student` (`id`),
  CONSTRAINT `FK2E2EF2DE6C8A2663` FOREIGN KEY (`teacher_id`) REFERENCES `teacher` (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

表中插入的記錄如下所示:

mysql> select * from teacher_student;
+------------+------------+
| teacher_id | student_id |
+------------+------------+
|          1 |          1 |
|          1 |          2 |
|          2 |          1 |
|          2 |          2 |
+------------+------------+
4 rows in set (0.00 sec)

 

程序中註釋爲1的語句非常重要,它是建立Teacher與Student關聯的語句,如果沒有這兩條語句,雖然程序照樣會執行,但是在中間表teacher_student沒有任何記錄,也就是Teacher與Student之間未關聯。

當然你也可以通過程序中註釋爲2的語句來建立Teacher與Student之間的關聯關係,同樣會產生與註釋爲1的語句的效果。但是你不能在程序中同時出現以上四句程序,否則會拋出異常( PRIMARY KEY (`student_id`,`teacher_id`),所以會出現主鍵衝突的異常 ),:

Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)
Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)
Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)
Hibernate: insert into teacher_student (teacher_id, student_id) values (?, ?)
Hibernate: insert into teacher_student (student_id, teacher_id) values (?, ?)
Hibernate: insert into teacher_student (student_id, teacher_id) values (?, ?)
Hibernate: insert into teacher_student (student_id, teacher_id) values (?, ?)
Hibernate: insert into teacher_student (student_id, teacher_id) values (?, ?)
Exception in thread "main" org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update

解決上面產生異常的辦法是設置inverse屬性。即在Tearcher一端或Student一端設置inverse="true",即讓他們之中的某一方放棄維護關聯關係。此時,雖然上面四句程序在測試程序中同時出現了(其實就是在對象模型上相互設置了他們的關聯關係),但程序照樣能運行正常,因爲了在數據庫模型上,只會有兩句程序生效,也就是沒有設置inverse="true"的那一端會去維護關聯關係。有關inverse的說細信息,可以參看我的文章hibernate級聯(cascade和inverse).

 

執行測試程序中的查詢測試,控制檯打印的信息如下所示:

Hibernate: select teacher0_.id as id5_0_, teacher0_.name as name5_0_ from Teacher teacher0_ where teacher0_.id=?
Hibernate: select students0_.teacher_id as teacher1_1_, students0_.student_id as student2_1_, student1_.id as id7_0_, student1_.name as name7_0_ from teacher_student students0_ left outer join Student student1_ on students0_.student_id=student1_.id where students0_.teacher_id=?
students:2

從打印出的sql語句可以看出,多對多關係進行查詢時,效率是比較低的。

發佈了38 篇原創文章 · 獲贊 7 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章