多对多关联是指两表之间,每一行数据可以有多个对应关系。
举个例子,学生选课,一个学生可以选择多门课程(相当于一对多关联),同时一门课又被多个学生选择(相当于一对多关联);一个学生对应着学生表中的一条数据,而这条数据又可以对应多门课程,同样,一个课程也对应多个学生。
在hibernate中通过中间表来实现多对多关联。
下面说说在hibernate中的实现
学生实体类
public class Student {
private Integer sid;
private String sname;
private String sex;
private Set<Course> courses = new HashSet<Course>();
//以下省略javabean方法
}
课程实体类
public class Course {
private Integer courseId;
private String courseName;
private String courseDesc;
private Set<Student> students = new HashSet<Student>();
//以下省略javabean方法
}
Course.hbm.xml配置
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.hibernate.many2many.entity">
<class name="Student">
<id name="sid" column="SID" type="java.lang.Integer">
<generator class="assigned"></generator>
</id>
<property name="sname" column="SNAME" not-null="true" type="java.lang.String"></property>
<property name="sex" column="SEX" type="java.lang.String"></property>
<!-- name属性指该学生实体持有课程集合,table属性指定该学生表连接哪张中间表.inverse将关联控制权交给学生表 -->
<set name="courses" table="STUDENTCHOOSECOURSE" inverse="true">
<!-- column属性指连接本表的STUDENTCHOOSECOURSE中间表所关联的外键列为RESID -->
<key column="RESID"></key>
<!-- 本表关联的Course表连接中间表STUDENTCHOOSECOURSE所对应的列RECOURSEID -->
<many-to-many class="Course" column="RECOURSEID"></many-to-many>
</set>
</class>
</hibernate-mapping>
Student.hbm.xml配置
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.hibernate.many2many.entity">
<class name="Student">
<id name="sid" column="SID" type="java.lang.Integer">
<generator class="assigned"></generator>
</id>
<property name="sname" column="SNAME" not-null="true" type="java.lang.String"></property>
<property name="sex" column="SEX" type="java.lang.String"></property>
<!-- 学生类持有课程类的集合,并且学生表和中间表STUDENTCHOOSECOURSE关联,学生实体对所有操作进行级联 -->
<set name="courses" table="STUDENTCHOOSECOURSE" cascade="all" >
<!-- 中间表通过RESID关联该学生表主键 -->
<key column="RESID"></key>
<!-- 学生类持有的该集合是课程类,多对多映射通过课程ID来对应到课程实体类 -->
<many-to-many class="Course" column="RECOURSEID"></many-to-many>
</set>
</class>
</hibernate-mapping>
测试代码
@Test
public void testAdd()
{
Student stu1 = new Student(1, "小明", "男");
Student stu2 = new Student(2, "小红", "女");
Student stu3 = new Student(3, "小兰", "女");
Course course1 = new Course(1003, "数学", "数学是xxxx");
Course course2 = new Course(1210, "语文", "语文是xxxx");
Course course3 = new Course(3212, "人工智能", "人工智能是xxxx");
//11,13,22,21,31,32,33
stu1.getCourses().add(course1);
stu1.getCourses().add(course3);
stu2.getCourses().add(course2);
stu2.getCourses().add(course1);
stu3.getCourses().add(course1);
stu3.getCourses().add(course2);
stu3.getCourses().add(course3);
session.save(stu1);
session.save(stu2);
session.save(stu3);
}
依次是hibernate创建的学生表、课程表、选课表。可以看到在课程表和学生表中是没有字段与彼此对应,关联关系全通过选课表来对应。
现在来讲讲inverse="true"和cascade="all"
inverse="true"是将关联控制权交给对方,自己不维护(用以提高数据库性能)
cascade="all"指对所有操作进行级联
当我们把关联控制权交给student表的时候,应该让student表拥有级联操作,否则将导致报错:外键约束异常。
之前我不太理解inverse="true"应该放在哪里使用,于是把inverse="true"和cascade="all"放在Course.hbm.xml中,导致出现下面异常
SQL Error: 1452, SQLState: 23000
org.hibernate.exception.ConstraintViolationException:
could not execute statement
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:
Cannot add or update a child row: a foreign key constraint fails (`db_ormlearn`.`studentchoosecourse`, CONSTRAINT `FK_hk3veey6orbgaiayejlmms7uf` FOREIGN KEY (`RECOURSEID`) REFERENCES `course` (`COURSEID`))
发现是外键约束异常,查看控制台,在向student中添加数据时,始终缺少指向课程的外键courseId,于是发现many-to-many可能有问题,调试一番,解决了。哈哈。
总结:
一定要注意,inverse和cascade属性,谁在维护关联关系,那么级联操作就应该交给谁
<many-to-many>中的属性介绍
<set name="students" table="STUDENTCHOOSECOURSE" inverse="true" >
<!-- key标签中,column属性指定连接这张表的中间表的外键 -->
<key column="RECOURSEID" ></key>
<!-- class连接这张表的多方的类名,column指通过中间表的那个字段连接到这张表 -->
<many-to-many class="Student" column="RESID"></many-to-many>
</set>
<set>中的name属性指定该hbm.xml对应的实体类所包含的集合,table表示要关联的表
<key>中的column指要关联的表通过哪个字段关联到本表。key是指外键约束
<many-to-many>指定另一张表要关联到中间表的信息。class指定另一张表的类名。column指关联到本中间表的字段