1. 程式人生 > >Hibernate 對映關係 ---Many2Many 雙向關聯

Hibernate 對映關係 ---Many2Many 雙向關聯

以Student和Course為例,一個學生可以選多門課程,一門課程也可以被多個學生選取;

持久化類Student:

[java]  view plain copy
  1. package bean;  
  2.   
  3. import java.util.Set;  
  4.   
  5. public class Student {  
  6.     private long id;  
  7.     private String name;//學生姓名  
  8.     private Set<Course> courses;//該學生選擇的課程  
  9.     //省略set、get方法  
  10. }  
持久化類Course:

[java]  view plain copy
  1. package bean;  
  2.   
  3. import java.util.Set;  
  4.   
  5. public class Course {  
  6.     private
     long id;  
  7.     private String name;//課程名稱  
  8.     private Set<Student> students;//選擇該課程的學生  
  9.         //省略set、get方法  
  10. }  
物件關係對映檔案Student.hbm.xml:

[html]  view plain copy
  1. <hibernate-mapping>  
  2.     <class name="bean.Student" table="students">  
  3.         <id name="id" column="id" type="long">  
  4.             <generator class="increment"></generator>  
  5.         </id>  
  6.         <property name="name" column="name" type="string"></property>  
  7.         <set name="courses" table="students_courses" cascade="save-update">   
  8.             <key column="student_id"></key>  
  9.             <many-to-many class="bean.Course" column="course_id"></many-to-many>  
  10.         </set>  
  11.     </class>  
  12. </hibernate-mapping>  

多對多關聯關係的實現需要一個連線表,<set>的屬性指出的就是連線表的名稱,<key>指出連線表參照students表id的外來鍵的欄位名;<many-to-many>中的class指定與Student多對多關聯的類,column指定連線表參照Course對映表(此處由Course.hbm.xml對映為courses表)id的外來鍵的欄位名,Course.hbm.xml中的<set>配置與Student.hbm.xml中<set>相反:

Course.hbm.xml:

[java]  view plain copy
  1. <hibernate-mapping>  
  2.     <class name="bean.Course" table="courses">  
  3.         <id name="id" column="id" type="long">  
  4.             <generator class="increment"></generator>  
  5.         </id>  
  6.         <property name="name" column="name" type="string"></property>  
  7.         <set name="students" table="students_courses" cascade="save-update" inverse="true">  
  8.             <key column="course_id"></key>  
  9.             <many-to-many class="bean.Student" column="student_id"></many-to-many>  
  10.         </set>  
  11.     </class>  
  12. </hibernate-mapping>  

注意:兩個對映檔案中設定的連線表的名稱以及連線表中的兩個欄位名需對應相同,如連線表名都為"students_courses"兩欄位為"student_id"和"course_id",否則會導致不必要的麻煩;連線表的主鍵為聯合主鍵(student_id,course_id)。

三個表的結構及對應關係如下所示:

儲存物件:

[java]  view plain copy
  1. Student s1=new Student();  
  2. s1.setName("lisi");  
  3. Course c1=new Course();  
  4. c1.setName("English");  
  5. Course c2=new Course();  
  6. c2.setName("science");  
  7. s1.setCourses(new HashSet<Course>());  
  8. c1.setStudents(new HashSet<Student>());  
  9. c2.setStudents(new HashSet<Student>());  
  10. s1.getCourses().add(c1);  
  11. s1.getCourses().add(c2);  
  12. c1.getStudents().add(s1);  
  13. c2.getStudents().add(s1);  
  14.   
  15. session.save(c1);  
  16. session.save(s1);  

(1)如果兩個對映檔案的inverse都設為false(預設),則會出現異常(主鍵重複)導致插入失敗:

org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update

Caused by: java.sql.BatchUpdateException: Duplicate entry '1-1' for key 'PRIMARY'

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException :Duplicate entry '1-1' for key 'PRIMARY'

解釋:應為兩對映檔案中的inverse都為true,則Student和Course都去維護關聯關係,即同時向連線表中插入記錄,則會導致主鍵重複而插入失敗。

解決辦法:

     ——將其中一方的inverse設為true,讓對方維持關聯關係;

     ——將s1.getCourses().add(c1);或 c1.getStudents().add(s1);刪除,因為若某個Course中的students集合為空時,它就不會去向連線表中新增記錄,也就不會與Student向連線表中插入記錄時衝突而主鍵重複。

(2)如果都設為true,則都不會向連線表中插入記錄而只是向兩表中插入記錄(兩者都認為對方會維持關聯關係)執行的SQl語句為:

[sql]  view plain copy
  1. Hibernate: insert into courses (name, id) values (?, ?)  
  2. Hibernate: insert into students (name, id) values (?, ?)  
  3. Hibernate: insert into courses (name, id) values (?, ?)  
(3)設一方的inverse為true,正常插入資料時輸出的SQL語句為:

[sql]  view plain copy
  1. Hibernate: insert into courses (name, id) values (?, ?)  
  2. Hibernate: insert into students (name, id) values (?, ?)  
  3. Hibernate: insert into courses (name, id) values (?, ?)  
  4. Hibernate: insert into students_courses (student_id, course_id) values (?, ?)  
  5. Hibernate: insert into students_courses (student_id, course_id) values (?, ?)  
刪除學生(Student)記錄:

[java]  view plain copy
  1. Student s=(Student)session.get(Student.class, 2L);  
  2. session.delete(s);  
注意:

    (1)如果不是Student維持關聯關係:

           ——若連線表students_courses中有參照students表中該記錄的記錄(即在students_courses表中存在student_id為2L的記錄)時,則刪除失敗。

           ——若連線表students_courses中沒有參照students表中該記錄的記錄時,則可以成功地將該記錄刪除。

     (2)如果是Student維持關聯關係:

           ——先將連線表students_courses中參照students表中該記錄的記錄刪除,然後將該學生記錄從students表中刪除


查詢某學生選的所有課程:

[java]  view plain copy
  1.           Student s=(Student)session.get(Student.class, 2L);  
  2.           Set<Course> set=s.getCourses();  
  3.             
  4.           for (Iterator iterator = set.iterator(); iterator.hasNext();) {  
  5. Course course = (Course) iterator.next();  
  6. System.out.println(course.getName());  

某學生又選了一門新課(增加了連線表中的一條記錄):

[java]  view plain copy
  1. Student s=(Student)session.get(Student.class, 2L);  
  2. Course c=(Course)session.get(Course.class,1L );  
  3. s.getCourses().add(c);  
  4. c.getStudents().add(s);  

刪除某學生的一條選課記錄(刪除了連線表中的一條記錄):

[java]  view plain copy
  1.      Student s=(Student)session.get(Student.class, 2L);  
  2. Course c=(Course)session.get(Course.class,1L );  
  3. s.getCourses().remove(c);  
(此時只會刪除連線表中的一條記錄而不會去修改Students和courses表中的記錄)


轉載出處:http://blog.csdn.net/jialinqiang/article/details/8698052