hibernate多對多(many-to-many)
hibernate多對多(many-to-many):在操作和效能方面都不太理想,所以多對多的對映使用較少,實際使用中最好轉換成一對多的物件模型;hibernate會為我們建立中間關聯表,轉換成兩個一對多。
1. E-R圖
2. 實體類:
Teacher實體類如下:
Java程式碼- packagecom.reiyen.hibernate.domain;
- importjava.util.Set;
- publicclassTeacher{
- privateintid;
- privateStringname;
- privateSet<Student>students;
- //setter和getter方法
- }
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程式碼- packagecom.reiyen.hibernate.domain;
- importjava.util.Set;
- publicclassStudent{
- privateintid;
- privateStringname;
- privateSet<Teacher>teachers;
- //setter和getter方法
- }
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如下:
- <?xmlversion="1.0"?>
- <!DOCTYPEhibernate-mappingPUBLIC
- "-//Hibernate/HibernateMappingDTD3.0//EN"
- "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
- <hibernate-mappingpackage="com.reiyen.hibernate.domain">
- <classname="Teacher">
- <idname="id">
- <generatorclass="native"/>
- </id>
- <propertyname="name"/>
- <!--通過table項告訴hibernate中間表的名稱-->
- <setname="students"table="teacher_student">
- <!--通過key屬性告訴hibernate在中間表裡面查詢teacher_id值相應的teacher記錄-->
- <keycolumn="teacher_id"/>
- <!--通過column項告訴hibernate對student表中查詢student_id值相就的studnet記錄-->
- <many-to-manyclass="Student"column="student_id"/>
- </set>
- </class>
- </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程式碼- <?xmlversion="1.0"?>
- <!DOCTYPEhibernate-mappingPUBLIC
- "-//Hibernate/HibernateMappingDTD3.0//EN"
- "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
- <hibernate-mappingpackage="com.reiyen.hibernate.domain">
- <classname="Student">
- <idname="id">
- <generatorclass="native"/>
- </id>
- <propertyname="name"/>
- <setname="teachers"table="teacher_student">
- <keycolumn="student_id"/>
- <many-to-manyclass="Teacher"column="teacher_id"/>
- </set>
- </class>
- </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程式碼- publicclassMany2Many{
- publicstaticvoidmain(String[]args){
- add();
- //query(1);
- }
- staticvoidquery(intid){
- Sessions=null;
- Transactiontx=null;
- try{
- s=HibernateUtil.getSession();
- tx=s.beginTransaction();
- Teachert=(Teacher)s.get(Teacher.class,id);
- System.out.println("students:"+t.getStudents().size());
- tx.commit();
- }finally{
- if(s!=null)
- s.close();
- }
- }
- staticvoidadd(){
- Sessions=null;
- Transactiontx=null;
- try{
- Set<Teacher>ts=newHashSet<Teacher>();
- Teachert1=newTeacher();
- t1.setName("t1name");
- ts.add(t1);
- Teachert2=newTeacher();
- t2.setName("t2name");
- ts.add(t2);
- Set<Student>ss=newHashSet<Student>();
- Students1=newStudent();
- s1.setName("s1");
- ss.add(s1);
- Students2=newStudent();
- 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();
- }
- }
- }
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語句可以看出,多對多關係進行查詢時,效率是比較低的。
本文來自轉載