hibernate如何处理双向多对多映射关系的?
双向 n-n 关联需要两端都使用集合属性
双向n-n关联必须使用连接表(即第三方表)
- 集合属性应增加 key 子元素用以映射外键列, 还应增加many-to-many子元素关联实体类
在双向 n-n 关联的两边都需指定连接表的表名及外键列的列名. 两个集合元素 set 的 table 元素的值必须指定,而且必须相同。
set元素的两个子元素:key 和 many-to-many 都必须指定 column 属性,其中,key 和 many-to-many 分别指定本持久化类和关联类在连接表中的外键列名,因此两边的 key 与 many-to-many 的column属性交叉相同。也就是说,一边的set元素的key的 cloumn值为a,many-to-many 的 column 为b;则另一边的 set 元素的 key 的 column 值 b,many-to-many的 column 值为 a. 对于双向 n-n 关联, 必须把其中一端的 inverse 设置为 true, 否则两端都维护关联关系可能会造成主键冲突.
解释代码如下:
Student.java
public class Student {
private Integer id;
private String name;
private Set<Course> courses = new HashSet<>();
//...
}
Course.java
public class Course {
private Integer id;
private String name;
private Set<Student> students = new
HashSet<>();
//...
}
Course.hbm.xml
<hibernate-mapping package="com.zc.cris.manytomany">
<class name="Course" table="COURSE">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="native" />
</id>
<property name="name" type="java.lang.String" >
<column name="NAME" />
</property>
<!-- 映射多对多的关系,需要指定一张中间表,并且需要有一方放弃关系维护 -->
<set name="students" table="STUDENT_COURSE" inverse="true">
<key>
<!-- 指定当前对象在中间表的列名 -->
<column name="COURSE_ID"></column>
</key>
<!-- 多对多关系映射,column指定set集合中的持久化类在中间表的名称 -->
<many-to-many class="Student" column="STUDENT_ID"></many-to-many>
</set>
</class>
</hibernate-mapping>
Student.hbm.xml
<hibernate-mapping package="com.zc.cris.manytomany">
<class name="com.zc.cris.manytomany.Student" table="STUDENTS">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="native" />
</id>
<property name="name" type="java.lang.String">
<column name="NAME" />
</property>
<set name="courses" table="STUDENT_COURSE">
<key>
<!-- 指定当前对象在中间表的列名 -->
<column name="STUDENT_ID"></column>
</key>
<!-- 多对多关系映射,column指定set集合中的持久化类在中间表的外键的名称 -->
<many-to-many class="Course" column="COURSE_ID"></many-to-many>
</set>
</class>
</hibernate-mapping>
TestHibernate.java
@Test
void testGet() {
//同样存在懒加载问题
Student student = this.session.get(Student.class, 1);
System.out.println(student.getName());
System.out.println(student.getCourses().getClass());
// select
// courses0_.STUDENT_ID as STUDENT_2_1_0_,
// courses0_.COURSE_ID as COURSE_I1_1_0_,
// course1_.ID as ID1_0_1_,
// course1_.NAME as NAME2_0_1_
// from
// STUDENT_COURSE courses0_
// inner join
// COURSE course1_
// on courses0_.COURSE_ID=course1_.ID
// where
// courses0_.STUDENT_ID=?
System.out.println(student.getCourses().size());
}
@Test
void testSave() {
Student student1 = new Student();
Student student2 = new Student();
student1.setName("a");
student2.setName("b");
Course course1 = new Course();
Course course2 = new Course();
course1.setName("aa");
course2.setName("bb");
course1.getStudents().add(student1);
course1.getStudents().add(student2);
course2.getStudents().add(student1);
course2.getStudents().add(student2);
student1.getCourses().add(course1);
student1.getCourses().add(course2);
student2.getCourses().add(course1);
student2.getCourses().add(course1);
this.session.save(student1);
this.session.save(student2);
this.session.save(course1);
this.session.save(course2);
}
测试结果如图: