Hibernate多对多双向关联
以Student和Course为例,一个学生可以选多门课程,一门课程也可以被多个学生选取;
持久化类Student:
- package bean;
- import java.util.Set;
- public class Student {
- private long id;
- private String name;//学生姓名
- private Set<Course> courses;//该学生选择的课程
- //省略set、get方法
- }
持久化类Course:
- package bean;
- import java.util.Set;
- public class Course {
- private long id;
- private String name;//课程名称
- private Set<Student> students;//选择该课程的学生
- //省略set、get方法
- }
对象关系映射文件Student.hbm.xml:
- <hibernate-mapping>
- <class name="bean.Student" table="students">
- <id name="id" column="id" type="long">
- <generator class="increment"></generator>
- </id>
- <property name="name" column="name" type="string"></property>
- <set name="courses" table="students_courses" cascade="save-update">
- <key column="student_id"></key>
- <many-to-many class="bean.Course" column="course_id"></many-to-many>
- </set>
- </class>
- </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:
- <hibernate-mapping>
- <class name="bean.Course" table="courses">
- <id name="id" column="id" type="long">
- <generator class="increment"></generator>
- </id>
- <property name="name" column="name" type="string"></property>
- <set name="students" table="students_courses" cascade="save-update" inverse="true">
- <key column="course_id"></key>
- <many-to-many class="bean.Student" column="student_id"></many-to-many>
- </set>
- </class>
- </hibernate-mapping>
注意:两个映射文件中设置的连接表的名称以及连接表中的两个字段名需对应相同,如连接表名都为"students_courses"两字段为"student_id"和"course_id",否则会导致不必要的麻烦;连接表的主键为联合主键(student_id,course_id)。
三个表的结构及对应关系如下所示:
保存对象:
- Student s1=new Student();
- s1.setName("lisi");
- Course c1=new Course();
- c1.setName("English");
- Course c2=new Course();
- c2.setName("science");
- s1.setCourses(new HashSet<Course>());
- c1.setStudents(new HashSet<Student>());
- c2.setStudents(new HashSet<Student>());
- s1.getCourses().add(c1);
- s1.getCourses().add(c2);
- c1.getStudents().add(s1);
- c2.getStudents().add(s1);
- session.save(c1);
- 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语句为:
- Hibernate: insert into courses (name, id) values (?, ?)
- Hibernate: insert into students (name, id) values (?, ?)
- Hibernate: insert into courses (name, id) values (?, ?)
(3)设一方的inverse为true,正常插入数据时输出的SQL语句为:
- Hibernate: insert into courses (name, id) values (?, ?)
- Hibernate: insert into students (name, id) values (?, ?)
- Hibernate: insert into courses (name, id) values (?, ?)
- Hibernate: insert into students_courses (student_id, course_id) values (?, ?)
- Hibernate: insert into students_courses (student_id, course_id) values (?, ?)
删除学生(Student)记录:
- Student s=(Student)session.get(Student.class, 2L);
- session.delete(s);
注意:
(1)如果不是Student维持关联关系:
——若连接表students_courses中有参照students表中该记录的记录(即在students_courses表中存在student_id为2L的记录)时,则删除失败。
——若连接表students_courses中没有参照students表中该记录的记录时,则可以成功地将该记录删除。
(2)如果是Student维持关联关系:
——先将连接表students_courses中参照students表中该记录的记录删除,然后将该学生记录从students表中删除
查询某学生选的所有课程:
- Student s=(Student)session.get(Student.class, 2L);
- Set<Course> set=s.getCourses();
- for (Iterator iterator = set.iterator(); iterator.hasNext();) {
- Course course = (Course) iterator.next();
- System.out.println(course.getName());
某学生又选了一门新课(增加了连接表中的一条记录):
- Student s=(Student)session.get(Student.class, 2L);
- Course c=(Course)session.get(Course.class,1L );
- s.getCourses().add(c);
- c.getStudents().add(s);
删除某学生的一条选课记录(删除了连接表中的一条记录):
- Student s=(Student)session.get(Student.class, 2L);
- Course c=(Course)session.get(Course.class,1L );
- s.getCourses().remove(c);
(此时只会删除连接表中的一条记录而不会去修改Students和courses表中的记录)
转载请注明出处:http://blog.csdn.net/jialinqiang/article/details/8698052
Hibernate多对多双向关联的更多相关文章
- hibernate多对一双向关联
关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...
- Java进阶知识12 Hibernate多对多双向关联(Annotation+XML实现)
1.Annotation 注解版 1.1.应用场景(Student-Teacher):当学生知道有哪些老师教,老师也知道自己教哪些学生时,可用双向关联 1.2.创建Teacher类和Student类 ...
- Hibernate多对多双向关联的配置
Hibernate的双向多对多关联有两种配置方法:那我们就来看看两种方案是如何配置的. 一.创建以各自类为类型的集合来关联 1.首先我们要在两个实体类(雇员<Emploee>.工程< ...
- Hibernate多对多双向关联需要注意的问题(实例说话)
以Student和Course为例,一个学生可以选多门课程,一门课程也可以被多个学生选取: 持久化类Student: package bean; import java.util.Set; publi ...
- hibernate 多对多双向关联
package com.bjsxt.hibernate; import java.util.HashSet; import java.util.Set; import javax.persistenc ...
- Java进阶知识10 Hibernate一对多_多对一双向关联(Annotation+XML实现)
本文知识点(目录): 1.Annotation 注解版(只是测试建表) 2.XML版 的实现(只是测试建表) 3.附录(Annotation 注解版CRUD操作)[注解版有个问题:插入值时 ...
- hibernate多对一单向关联
关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...
- Java进阶知识11 Hibernate多对多单向关联(Annotation+XML实现)
1.Annotation 注解版 1.1.应用场景(Student-Teacher):当学生知道有哪些老师教,但是老师不知道自己教哪些学生时,可用单向关联 1.2.创建Teacher类和Student ...
- Java进阶知识08 Hibernate多对一单向关联(Annotation+XML实现)
1.Annotation 注解版 1.1.在多的一方加外键 1.2.创建Customer类和Order类 package com.shore.model; import javax.persisten ...
随机推荐
- PHP+Mysql+jQuery实现地图区域数据统计-展示数据
我们要在地图上有限的区块内展示更多的信息,更好的办法是通过地图交互来实现.本文将给大家讲解通过鼠标滑动到地图指定省份区域,在弹出的提示框中显示对应省份的数据信息.适用于数据统计和地图区块展示等场景. ...
- Ubuntu 16.04 风扇特别响解决办法
主要原因是显卡驱动的问题,选择驱动专有,tested.
- android模拟器停在Waiting for HOME解决方案
直接打开Android SDK Manager然后再从Android SDK Manager里的tools打开Android AVD Manager,删除掉在Eclipse里创建的模拟器.并在新建一个 ...
- ( C++) Access the hard drive.
// Open up the volume HANDLE hVolume = CreateFile(wstrPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHA ...
- NPOIHelper
public class NPOIHelper { public static void WriteDataToExceel(string fileName, DataSet ds) { if (Fi ...
- C#位操作(转)
在C#中可以对整型运算对象按位进行逻辑运算.按位进行逻辑运算的意义是:依次取被运算对象的每个位,进行逻辑运算,每个位的逻辑运算结果是结果值的每个位.C#支持的位逻辑运算符如表2.9所示. 算符号 意义 ...
- oleDB 导入excel遇到的问题
本地测试一切ok. 到服务器上测试各种报错,先装了office2010,又装了AccessDatabaseEngine,之后报"Microsoft Excel 不能访问文件 可能的原因有: ...
- 三维空间旋转和Three.JS中的实现
三维空间中主要有两种几何变换,一种是位置的变换,位置变换和二维空间的是一样的.假设一点P(X1,Y1,Z1) 移动到Q(X2,Y2,Z2)只要简单的让P点的坐标值加上偏移值就可以了.但是三维空间的旋转 ...
- linux命令-cp/scp {拷贝}
一 命令解释 名称:cp 使用权限:所有使用者 使用方式: cp [options] source dest cp [options] source... directory 命令参数: -a 尽可能 ...
- get_list_or_404(klass, *args, **kwargs)和get_object_or_404(klass, *args, **kwargs)区别
get_object_or_404() 是通过调用get()方法从model管理器上获取数据, 如果对象不存在,它会报Http404的异常,而不是model的 DoseNotExist异常. get_ ...