以Student和Course为例,一个学生可以选多门课程,一门课程也可以被多个学生选取;

持久化类Student:

  1. package bean;
  2. import java.util.Set;
  3. public class Student {
  4. private long id;
  5. private String name;//学生姓名
  6. private Set<Course> courses;//该学生选择的课程
  7. //省略set、get方法
  8. }

持久化类Course:

  1. package bean;
  2. import java.util.Set;
  3. public class Course {
  4. private long id;
  5. private String name;//课程名称
  6. private Set<Student> students;//选择该课程的学生
  7. //省略set、get方法
  8. }

对象关系映射文件Student.hbm.xml:

  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:

  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)。

三个表的结构及对应关系如下所示:

保存对象:

  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. session.save(c1);
  15. 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语句为:

  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语句为:

  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)记录:

  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表中删除

查询某学生选的所有课程:

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

某学生又选了一门新课(增加了连接表中的一条记录):

  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);

删除某学生的一条选课记录(删除了连接表中的一条记录):

  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

Hibernate多对多双向关联的更多相关文章

  1. hibernate多对一双向关联

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  2. Java进阶知识12 Hibernate多对多双向关联(Annotation+XML实现)

    1.Annotation 注解版 1.1.应用场景(Student-Teacher):当学生知道有哪些老师教,老师也知道自己教哪些学生时,可用双向关联 1.2.创建Teacher类和Student类 ...

  3. Hibernate多对多双向关联的配置

    Hibernate的双向多对多关联有两种配置方法:那我们就来看看两种方案是如何配置的.  一.创建以各自类为类型的集合来关联 1.首先我们要在两个实体类(雇员<Emploee>.工程< ...

  4. Hibernate多对多双向关联需要注意的问题(实例说话)

    以Student和Course为例,一个学生可以选多门课程,一门课程也可以被多个学生选取: 持久化类Student: package bean; import java.util.Set; publi ...

  5. hibernate 多对多双向关联

    package com.bjsxt.hibernate; import java.util.HashSet; import java.util.Set; import javax.persistenc ...

  6. Java进阶知识10 Hibernate一对多_多对一双向关联(Annotation+XML实现)

    本文知识点(目录): 1.Annotation 注解版(只是测试建表)    2.XML版 的实现(只是测试建表)    3.附录(Annotation 注解版CRUD操作)[注解版有个问题:插入值时 ...

  7. hibernate多对一单向关联

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  8. Java进阶知识11 Hibernate多对多单向关联(Annotation+XML实现)

    1.Annotation 注解版 1.1.应用场景(Student-Teacher):当学生知道有哪些老师教,但是老师不知道自己教哪些学生时,可用单向关联 1.2.创建Teacher类和Student ...

  9. Java进阶知识08 Hibernate多对一单向关联(Annotation+XML实现)

    1.Annotation 注解版 1.1.在多的一方加外键 1.2.创建Customer类和Order类 package com.shore.model; import javax.persisten ...

随机推荐

  1. [Linux] - Docker pure-ftp使用

    Docker使用pure-ftp的方法及配置 1.用docker下载pure-ftp docker pull stilliard/pure-ftpd:hardened 2.下载完后直接运行 docke ...

  2. python3-day1-python简介及入门

    python简介及入门 python简介 Python的创始人为Guido van Rossum.1989年圣诞节期间,在阿姆斯特丹,Guido为了打发圣诞节的无趣,决心开发一个新的脚本解释程序,做为 ...

  3. 虚拟现实外包公司— VR开发编辑器意义重大 印证VR不仅服务于用户

    三大引擎制造商 Unity Technologies .Epic Games 和Crytek 讨论在自家产品中添加附件,方便开发商在虚拟现实中创作游戏.这会对许多人造成影响,特别是早已进入虚拟现实的先 ...

  4. MySql开启慢查询报错:Could not open /var/log/slow_query.log for logging (error 13).

    Turning logging off for the whole duration of the MySQL server process. File '/var/log/slow_query.lo ...

  5. C# 使用ffmpeg.exe进行音频转换完整demo-asp.net转换代码

    C# 使用ffmpeg.exe进行音频转换完整demo-asp.net转换代码 上一篇说了在winform下进行调用cmd.exe执行ffmpeg.exe进行音频转换完整demo.后来我又需要移植这个 ...

  6. Html5上传后有所见图片效果前端代码实现

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. 理解Condition的用法

    这个示例中BoundedBuffer是一个固定长度的集合,这个在其put操作时,如果发现长度已经达到最大长度,那么会等待notFull信号,如果得到notFull信号会像集合中添加元素,并发出notE ...

  8. 动画--android图片点击放大动画,并遮挡旁边的控件

    http://blog.csdn.net/s13488941815/article/details/40649823: 首先是点击放大可以使用android自带的缩放动画,因为要遮盖其他控件,就需要控 ...

  9. jquery 获取一组元素的选中项 - 函数、jquery获取复选框值、jquery获取单选按钮值

    做表单提交时,如果现在还在用form提交,用户体验很差,所以一般使用ajax提交. 其中需要获取每个表单输入元素的值,获取的时候像文本框这些还好说,Jquery提供了 .val() 方法,获取很方便, ...

  10. php 运行脚本shell

    F:\phpStudy\php53\php.exe -f F:\phpStudy\WWW\qh\qh.php /usr/local/php/bin/php -f test.php Usage: php ...