文档章节

Hibernate多对多双向关联

志强朱
 志强朱
发布于 2016/05/08 17:45
字数 1137
阅读 2
收藏 0

以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.          <!--students_courses 是中间表-->
  8.         <set name="courses" table="students_courses" cascade="save-update">   
  9.             <key column="student_id"></key>  
  10.             <many-to-many class="bean.Course" column="course_id"></many-to-many>  
  11.         </set>  
  12.     </class>  
  13. </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表中的记录)

© 著作权归作者所有

共有 人打赏支持
志强朱
粉丝 4
博文 209
码字总数 108290
作品 0
郑州
程序员
私信 提问
NHibernate从入门到精通系列(10)——多对多关联映射

内容摘要 单向多对多关联映射 双向多对多关联映射 一、单向多对多关联映射 1.1 多对多关联映射描述 众所周知,持久化类的有三种对应关系:“一对一”、“一对多(多对一)”和“多对多”。在...

长平狐
2012/06/11
398
0
Hibernate映射——多对多关联映射(八)

映射原理 不论是单向关联还是双向关联都是通过第三张表,将两个表中的主键放到第三张做一个关联。用第三张表来解决可能会造成数据冗余的问题。 举例 一个用户(User)对多个角色(Role),一...

architect刘源源
2018/01/11
2
0
Hibernate-Annotation

课程地址 使用hibernate注解的目的 为了简化ORM映射文件(*.hbm)繁琐的配置。 注解需要做的是映射文件要做的。 JPA与Hibernate Java Persistence API Java持久化接口 JPA是标准接口,HIberna...

FutaoSmile丶
2017/09/28
0
0
Hibernate的映射

2.1、映射简介 对于所有的对象实体而言,有如下三种关系: 1:1,1:n,m:n 一种双向,一种单向。 2.2、一对多映射 多对一单向:many-to-one单向,指的是在多的这一端增加关联。 配置文件的...

pmos
2016/10/17
21
0
Hibernate中的cascade和inverse

这两个属性都用于一多对或者多对多的关系中。而inverse特别是用于双向关系,在单向关系中我们并不需要。 Cascade代表是否执行级联操作,Inverse代表是否由己方维护关系。 Cascade: Cascade属...

roockee
2012/12/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

nacos之springboot

本地操作系统:ubuntu18,我使用docker的方式启动nacos服务 docker image方式启动nacos 在docker hub上可以搜到nacos-server List-1.1 拉去最新的版本 mjduan@mjduan-ubuntu:/opt/software$ ...

克虏伯
21分钟前
2
0
指针数组和数组指针的区别

这两个名字不同当然所代表的意思也就不同。我刚开始看到这就吓到了,主要是中文太博大精深了,整这样的简称太专业了,把人都绕晕了。从英文解释或中文全称看就比较容易理解。 指针数组:arr...

天王盖地虎626
53分钟前
3
0
Qt那些事0.0.18

今天要记一下Qt中的Resource。自我感觉理解的不错,但是还会难免有谬误,在日后有可能会更新,也有可能不会。 小声的念叨一句,女人心,海底针。 今天就直接跳过了关于QML在qrc文件中的介绍,...

Ev4n
今天
2
0
深入解析js的作用域、预解析机制

虽然,ES6在我们工作中应用得越来越广泛,但是还是很多项目保留着ES5的写法,所以,今天,带着大家重新巩固下ES5下的作用域及预解析机制。 概念: 作用域:域,指的是一个空间、范围、区域,...

前端攻城老湿
今天
2
0
Spring Cloud Feign - 声明式 REST Client

1、Feign是什么 声明式REST client,来自NetFlix。 允许你编写无实现代码调用REST services 替换RestTemplate(甚至更简单) Spring Cloud 为使用Feign提供了包装器 2、怎样使用Feign 对比:...

Benz001
今天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部