hibernate系列(三)多对多的关联关系

简介:
以Teacher和Student为例,他们之间是多对多的关系。 
手动创建的数据库的三张表为,teacher、student、teacher_student。分别如下:
 
?
1
2
3
4
5
CREATE TABLE `teacher` (
   `id` int (11) NOT NULL AUTO_INCREMENT,
   ` name ` varchar (45) DEFAULT '' ,
   PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

?
1
2
3
4
5
CREATE TABLE `student` (
   `id` int (11) NOT NULL AUTO_INCREMENT,
   ` name ` varchar (45) DEFAULT '' ,
   PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

?
1
2
3
4
5
6
CREATE TABLE `teacher_student` (
   `id` int (11) NOT NULL AUTO_INCREMENT,
   `teacher_id` int (11) DEFAULT NULL ,
   `student_id` int (11) DEFAULT NULL ,
   PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Teacher类如下:  
?
1
2
3
4
5
6
7
public class Teacher {
 
     private Long id;
     private String name;
     private Set<Student> students;
//省略get、set方法
}

Teacher类对应的映射文件Teacher.hbm.xml:  
?
1
2
3
4
5
6
7
8
9
10
11
12
< hibernate-mapping >
     < class name = "com.ligang.domain.Teacher" table = "teacher" >
         < id name = "id" column = "id" type = "long" >
             < generator class = "identity" />
         </ id >
         < property name = "name" column = "name" type = "string" />
         < set name = "students" table = "teacher_student" >
             < key column = "teacher_id" ></ key >
             < many-to-many class = "com.ligang.domain.Student" column = "student_id" ></ many-to-many >
         </ set >
     </ class >
</ hibernate-mapping >

其中set标签的name指的是Teacher类的students属性,table指的是该属性要关联到哪张表。内部的key标签的column指的是该Teacher类所属的表的主键id作为teacher_student的外键teacher_id值。<many-tomany>标签指的是Student类所属的student表的主键作为teacher_student表的student_id值。 
Stusent类如下:
 
?
1
2
3
4
5
6
7
public class Student {
 
     private Long id;
     private String name;
     private Set<Teacher> teachers;
//省略get、set方法
}

Student类对应的映射文件Student.hbm.xml如下:  
?
1
2
3
4
5
6
7
8
9
10
11
12
< hibernate-mapping >
     < class name = "com.ligang.domain.Student" table = "student" >
         < id name = "id" column = "id" type = "long" >
             < generator class = "identity" />
         </ id >
         < property name = "name" column = "name" type = "string" />
         < set name = "teachers" table = "teacher_student"  inverse = "true" >
             < key column = "student_id" ></ key >
             < many-to-many class = "com.ligang.domain.Teacher" column = "teacher_id" ></ many-to-many >
         </ set >
     </ class >
</ hibernate-mapping >

其中的set映射关系同上。 
然后就来看下增添方法:
 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Test
     public void testAddTeacher1(){
         Session session=hibernateDao.getSession();
         Transaction tx=session.beginTransaction();
         
         Teacher t= new Teacher();
         t.setName( "teacher4" );
         
         Student s1= new Student();
         s1.setName( "assa" );
         
         Student s2= new Student();
         s2.setName( "sdfvdv" );
         
         Set<Student> students= new HashSet<Student>();
         students.add(s1);
         students.add(s2);
         
         t.setStudents(students);
         
         session.save(s1);
         session.save(s2);
         session.save(t);
         
         tx.commit();
         session.close();
     }

三个save,三个insert语句,同时,由于t.setStudents(students),所以teacher会去维护teacher_student关系表,所以又会增添两条insert语句,sql如下:  
?
1
2
3
4
5
Hibernate: insert into hibernate.student ( name ) values (?)
Hibernate: insert into hibernate.student ( name ) values (?)
Hibernate: insert into hibernate.teacher ( name ) values (?)
Hibernate: insert into hibernate.teacher_student (teacher_id, student_id) values (?, ?)
Hibernate: insert into hibernate.teacher_student (teacher_id, student_id) values (?, ?)

如果增添改为如下状态:  
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Test
     public void testAddTeacher(){
         Session session=hibernateDao.getSession();
         Transaction tx=session.beginTransaction();
         
         Teacher t= new Teacher();
         t.setName( "teacher4" );
         
         Student s1= new Student();
         s1.setName( "assa" );
         
         Student s2= new Student();
         s2.setName( "sdfvdv" );
         
         Set<Student> students= new HashSet<Student>();
         Set<Teacher> teachers= new HashSet<Teacher>();
         students.add(s1);
         students.add(s2);
         teachers.add(t);
         
         t.setStudents(students);
         s1.setTeachers(teachers);
         s2.setTeachers(teachers);
         
         session.save(s1);
         session.save(s2);
         session.save(t);
         
         tx.commit();
         session.close();
     }

此时,不仅建立起了t.setStudents(students)关系,同时s1.setTeachers(teachers);s2.setTeachers(teachers);也建立了关系,所以最终事务提交的时候,teacher、student都会去维护teacher_student表,造成了4个insert语句,造成了重复。如下:  
?
1
2
3
4
5
6
7
Hibernate: insert into hibernate.student (name) values (?)
Hibernate: insert into hibernate.student (name) values (?)
Hibernate: insert into hibernate.teacher (name) values (?)
Hibernate: insert into hibernate.teacher_student (student_id, teacher_id) values (?, ?)
Hibernate: insert into hibernate.teacher_student (student_id, teacher_id) values (?, ?)
Hibernate: insert into hibernate.teacher_student (teacher_id, student_id) values (?, ?)
Hibernate: insert into hibernate.teacher_student (teacher_id, student_id) values (?, ?)

为了避免这一现象,就需要某一方放弃维护teacher_student表的权限。如student放弃维护这一关系,使用inverse="true",如下:  
?
1
2
3
4
5
6
7
8
9
10
11
12
< hibernate-mapping >
     < class name = "com.ligang.domain.Student" table = "student" >
         < id name = "id" column = "id" type = "long" >
             < generator class = "identity" />
         </ id >
         < property name = "name" column = "name" type = "string" />
         < set name = "teachers" table = "teacher_student" inverse = "true" >
             < key column = "student_id" ></ key >
             < many-to-many class = "com.ligang.domain.Teacher" column = "teacher_id" ></ many-to-many >
         </ set >
     </ class >
</ hibernate-mapping >

此时再按照上述方法增添就只有teacher去维护teacher_student表了,不会造成重复。 

再看下多对多关联关系的查询:
 
?
1
2
3
4
5
6
7
8
9
10
11
12
@Test
     public void getTeacher(){
         Session session=hibernateDao.getSession();
         Transaction tx=session.beginTransaction();
         
         Teacher t=(Teacher) session.get(Teacher. class ,3L);
         System.out.println(t.getName());
         System.out.println(t.getStudents().size());
         
         tx.commit();
         session.close();
     }

这里同样存在这查询策略问题,此时Teacher类的映射文件对应的set标签的lazy属性有三个值,一个true、false、extra。默认为true,即实行延迟加载的策略,用到Student时才回去加载它,如下sql:  
?
1
2
3
4
Hibernate: select teacher0_.id as id1_3_0_, teacher0_. name as name2_3_0_ from hibernate.teacher teacher0_ where teacher0_.id=?
teacher2
Hibernate: select students0_.teacher_id as teacher_1_3_0_, students0_.student_id as student_2_4_0_, student1_.id as id1_2_1_, student1_. name as name2_2_1_ from hibernate.teacher_student students0_ inner join hibernate.student student1_ on students0_.student_id=student1_.id where students0_.teacher_id=?
2

若设置为false,则表示在加载Teacher时立即去加载相关的Student,如下sql:  
?
1
2
3
4
Hibernate: select teacher0_.id as id1_3_0_, teacher0_. name as name2_3_0_ from hibernate.teacher teacher0_ where teacher0_.id=?
Hibernate: select students0_.teacher_id as teacher_1_3_0_, students0_.student_id as student_2_4_0_, student1_.id as id1_2_1_, student1_. name as name2_2_1_ from hibernate.teacher_student students0_ inner join hibernate.student student1_ on students0_.student_id=student1_.id where students0_.teacher_id=?
teacher2
2

若设置为extra,则会更加智能化一些,即上述t.getStudents().size()并没有去访问Student的实际内容,仅仅是想获取数量,所以sql语句如下:  
?
1
2
3
4
Hibernate: select teacher0_.id as id1_3_0_, teacher0_. name as name2_3_0_ from hibernate.teacher teacher0_ where teacher0_.id=?
teacher2
Hibernate: select count (student_id) from hibernate.teacher_student where teacher_id =?
2

再看下更新Teacher:  
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Test
     public void updateTeacher(){
         Session session=hibernateDao.getSession();
         Transaction tx=session.beginTransaction();
         
         Teacher t=(Teacher) session.get(Teacher. class ,6L);
         
         Student s1= new Student();
         s1.setName( "assa" );
         
         Student s2= new Student();
         s2.setName( "sdfvdv" );
         
         Set<Student> students= new HashSet<Student>();
         students.add(s1);
         students.add(s2);
         
         t.setStudents(students);
         
         session.save(s1);
         session.save(s2);
         
         tx.commit();
         session.close();
     }

更新的时候,会先删除之前的teacher_student表中的相关记录,然后再新增新的记录,如下sql:  
?
1
2
3
4
5
6
Hibernate: select teacher0_.id as id1_3_0_, teacher0_.name as name2_3_0_ from hibernate.teacher teacher0_ where teacher0_.id=?
Hibernate: insert into hibernate.student (name) values (?)
Hibernate: insert into hibernate.student (name) values (?)
Hibernate: delete from hibernate.teacher_student where teacher_id=?
Hibernate: insert into hibernate.teacher_student (teacher_id, student_id) values (?, ?)
Hibernate: insert into hibernate.teacher_student (teacher_id, student_id) values (?, ?)


相关文章
|
18天前
|
Java 数据库连接 API
解锁高效开发秘籍:深入探究 Hibernate 如何优雅处理一对多与多对多关系,让数据映射再无烦恼!
【9月更文挑战第3天】Hibernate 是 Java 领域中最流行的 ORM 框架之一,广泛用于处理实体对象与数据库表之间的映射。尤其在处理复杂关系如一对多和多对多时,Hibernate 提供了丰富的 API 和配置选项。本文通过具体代码示例,展示如何使用 `@OneToMany`、`@JoinColumn`、`@ManyToMany` 和 `@JoinTable` 等注解优雅地实现这些关系,帮助开发者保持代码简洁的同时确保数据一致性。
27 4
|
4月前
|
Java 数据库连接 数据库
hibernate多对多、正向工程创建数据表——访问温馨提示
hibernate多对多、正向工程创建数据表——访问温馨提示
|
SQL XML 存储
Hibernate框架【五】——基本映射——多对多映射
Hibernate框架【五】——基本映射——多对多映射
168 0
|
4月前
|
机器学习/深度学习 SQL Java
Hibernate - 多对多关联关系映射
Hibernate - 多对多关联关系映射
54 0
|
Java 数据库连接 数据库
hibernate多对多、正向工程创建数据表——访问温馨提示
hibernate多对多、正向工程创建数据表——访问温馨提示
|
XML Java 数据库连接
《Hibernate上课笔记》-----class7----Hibernate实现多对多关联映射
《Hibernate上课笔记》-----class7----Hibernate实现多对多关联映射
87 0
《Hibernate上课笔记》-----class7----Hibernate实现多对多关联映射
|
Java 数据库连接 网络安全
【SSH快速进阶】——Hibernate 多对多映射
说到多对多关系,印象最深刻的就是大学的选修课。**一个学生可以选修多门课程,一门课程可以有多个学生选修,学生所选的每一门课程还有成绩**。这个场景的E-R图如下:
【SSH快速进阶】——Hibernate 多对多映射
|
SQL Oracle Java
hibernate(四) 双向多对多映射关系
现在继上一章节的一对多的映射关系讲解后,今天来讲讲多对多的映射关系把,明白了一对多,多对多个人感觉还是比较容易的,需要理清楚其数据库关系图,那么你就拿下了它。映射文件的配置还是那么些死东西。
154 0
|
Java 关系型数据库 数据库连接
Hibernate-ORM:12.Hibernate中的多对多关联关系
    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥-------------       本篇博客将讲述Hibernate中的多对多关联关系的操作,准备的篇幅较少,望海涵   一,讲述多对多   多对多的关联关系,比如学生和教师来说,一个学生由多个教师授课...
1266 0