zoukankan      html  css  js  c++  java
  • Hibernate多对多双向关联需要注意的问题(实例说话)

    以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

  • 相关阅读:
    四层架构设计实践
    看看node.js chat程序如何实现Ajax longpolling长链接刷新模式
    模仿igoogle【定制化、拖动排序,最大化、分屏】
    安装和配置Apache
    好书推荐《Pro ASP.NET MVC 3 Framework 3rd Edition》
    GAC和VS引用的程序集不一致?
    不要在 ASP.NET 4.5 Beta 的 Page 类事件上直接使用 async 与 await
    使用事务自动回滚来实现单元测试
    C# 如何异步查询数据库
    Linq + Jquery + Ajax 实现异步分页,批量删除,单个删除,全选,反选 ……
  • 原文地址:https://www.cnblogs.com/zhangshitong/p/5280816.html
Copyright © 2011-2022 走看看