多对多的实体关系模型也是很常见的,比如学生和课程的关系。一个学生可以选修多门课程,一个课程可以被多名学生选修。在关系型数据库中对于多对多关联关系的处理一般采用中间表的形式,将多对多的关系转化成两个一对多的关系。
Student类
public class Student implements Serializable { private Integer id; private String name; private Integer age; private Set<Course> courses = new HashSet<Course>(); }
Student.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="rock.lee.bean.Student" table="student" catalog="test"> <id name="id" column="id" type="int"> <generator class="native"></generator> </id> <property name="name" column="name" type="java.lang.String"></property> <property name="age" column="age" type="int"></property> <set name="courses" table="student_course"> <key column="student_id"></key> <many-to-many class="rock.lee.bean.Course" column="course_id"></many-to-many> </set> </class> </hibernate-mapping>
Course类
public class Course implements Serializable { private Integer id; private String name; private Set<Student> students = new HashSet<Student>(); }
Course.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="rock.lee.bean.Course" table="course" catalog="test"> <id name="id" column="id" type="int"> <generator class="native"></generator> </id> <property name="name" column="name" type="java.lang.String"></property> <!-- 设置关系表 --> <set name="students" table="student_course"> <!-- 关系表中的列名 --> <key column="course_id"></key> <!-- 配置关联对象,关联对象在关系表中的列名 --> <many-to-many class="rock.lee.bean.Student" column="student_id"></many-to-many> </set> </class> </hibernate-mapping>
案例一:测试保存
@Test public void testManyToMany01() { Session session = HibernateUtils.openSession(); Transaction transaction = session.beginTransaction(); Student s1 = new Student("小明", 11); Course c1 = new Course("英语"); //建立学生与课程的关系 s1.getCourses().add(c1); c1.getStudents().add(s1); session.save(s1); session.save(c1); transaction.commit(); session.close(); }
由于学生与课程,课程与学生都建立了关系,这种双向关系在保存是都会像中间表中插入数据,会抛出异常
Caused by: java.sql.BatchUpdateException: Duplicate entry '1-1' for key 'PRIMARY' Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '1-1' for key 'PRIMARY'
因为中间表主键重复了
@Test public void testManyToMany01() { Session session = HibernateUtils.openSession(); Transaction transaction = session.beginTransaction(); Student s1 = new Student("小明", 11); Course c1 = new Course("英语"); //建立学生与课程的关系 s1.getCourses().add(c1); // c1.getStudents().add(s1); session.save(s1); session.save(c1); transaction.commit(); session.close(); }
建立学生与课程的关系,不建立课程与学生关系
案例二:解除学生和课程之间的关系
mysql> select * from student; +----+------+------+ | id | name | age | +----+------+------+ | 2 | 小明 | 11 | +----+------+------+ 1 row in set (0.00 sec) mysql> select * from course; +----+------+ | id | name | +----+------+ | 2 | 英语 | +----+------+ 1 row in set (0.02 sec) mysql> select * from student_course; +------------+-----------+ | student_id | course_id | +------------+-----------+ | 2 | 2 | +------------+-----------+ 1 row in set (0.00 sec)
解除学生与课程的关系
@Test public void testManyToMany03() { Session session = HibernateUtils.openSession(); Transaction transaction = session.beginTransaction(); Student s1 = (Student) session.get(Student.class, 2); Course c1 = (Course) session.get(Course.class, 2); s1.getCourses().remove(c1); transaction.commit(); session.close(); }
persistent状态的对象只要一方解除关系,会自动删除关系表中的数据
Hibernate: delete from student_course where student_id=?
案例三:删除有选课的学生
mysql> select * from student_course; +------------+-----------+ | student_id | course_id | +------------+-----------+ | 3 | 3 | +------------+-----------+ 1 row in set (0.00 sec)
删除ID为3的学生
@Test public void testManyToMany04() { Session session = HibernateUtils.openSession(); Transaction transaction = session.beginTransaction(); Student s1 = (Student) session.get(Student.class, 3); session.delete(s1); transaction.commit(); session.close(); }
Hibernate会先删除studnet_course表中的数据,在删除student表中的数据
Hibernate: delete from student_course where student_id=? Hibernate: delete from test.student where id=?
在Student.hbm.xml配置casecade,删除学生时,会同时删除中间表中数据和课程数据