一对多

例:一个班级可以有多个学生,而一个学生只能属于一个班级。

模型

  1. package com.zze.bean;
  2.  
  3. import java.util.HashSet;
  4. import java.util.Set;
  5.  
  6. public class Class {
  7. private Integer id;
  8. private String name;
  9.  
  10. private Set<Student> students = new HashSet<>();
  11.  
  12. public Integer getId() {
  13. return id;
  14. }
  15.  
  16. public void setId(Integer id) {
  17. this.id = id;
  18. }
  19.  
  20. public String getName() {
  21. return name;
  22. }
  23.  
  24. public void setName(String name) {
  25. this.name = name;
  26. }
  27.  
  28. public Set<Student> getStudents() {
  29. return students;
  30. }
  31.  
  32. public void setStudents(Set<Student> students) {
  33. this.students = students;
  34. }
  35. }

班级:com.zze.bean.Class

  1. package com.zze.bean;
  2.  
  3. import java.util.Date;
  4.  
  5. public class Student {
  6. private Integer id;
  7. private String name;
  8. private Integer age;
  9. private Date birthday;
  10. private String address;
  11.  
  12. private Class clazz;
  13.  
  14. public Integer getId() {
  15. return id;
  16. }
  17.  
  18. public void setId(Integer id) {
  19. this.id = id;
  20. }
  21.  
  22. public String getName() {
  23. return name;
  24. }
  25.  
  26. public void setName(String name) {
  27. this.name = name;
  28. }
  29.  
  30. public Integer getAge() {
  31. return age;
  32. }
  33.  
  34. public void setAge(Integer age) {
  35. this.age = age;
  36. }
  37.  
  38. public Date getBirthday() {
  39. return birthday;
  40. }
  41.  
  42. public void setBirthday(Date birthday) {
  43. this.birthday = birthday;
  44. }
  45.  
  46. public String getAddress() {
  47. return address;
  48. }
  49.  
  50. public void setAddress(String address) {
  51. this.address = address;
  52. }
  53.  
  54. public Class getClazz() {
  55. return clazz;
  56. }
  57.  
  58. public void setClazz(Class clazz) {
  59. this.clazz = clazz;
  60. }
  61. }

学生:com.zze.bean.Student

配置文件

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
  5.  
  6. <hibernate-mapping>
  7. <class name="com.zze.bean.Class" table="class">
  8. <id name="id">
  9. <generator class="native"/>
  10. </id>
  11. <property name="name" length="32"/>
  12. <!--
  13. set :
  14. name : ‘多’的一方在当前类中的属性名
  15. -->
  16. <set name="students">
  17. <!--
  18. key :
  19. column : 外键表中的关联当前类对应表的外键名称
  20. -->
  21. <key column="cid"/>
  22. <!--
  23. one-to-many : 标识一对多关系
  24. class : ‘多’的一方全限定类名
  25. -->
  26. <one-to-many class="com.zze.bean.Student"/>
  27. </set>
  28. </class>
  29. </hibernate-mapping>

com/zze/bean/Class.hbm.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
  5.  
  6. <hibernate-mapping>
  7. <class name="com.zze.bean.Student" table="student">
  8. <id name="id">
  9. <generator class="native"/>
  10. </id>
  11. <property name="name" length="32"/>
  12. <property name="age"/>
  13. <property name="birthday"/>
  14. <property name="address"/>
  15. <!--
  16. many-to-one : 标识多对一关系
  17. name : ‘一’的一方在当前类中属性名
  18. column : 外键名称
  19. class : ‘一’的一方的全限定类名
  20. -->
  21. <many-to-one name="clazz" column="cid" class="com.zze.bean.Class"/>
  22. </class>
  23. </hibernate-mapping>

com/zze/bean/Student.hbm.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE hibernate-configuration PUBLIC
  3. "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
  4. "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
  5. <hibernate-configuration>
  6. <session-factory>
  7. <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
  8. <property name="hibernate.connection.url">jdbc:mysql://192.168.208.153:3306/test</property>
  9. <property name="hibernate.connection.username">root</property>
  10. <property name="hibernate.connection.password">root</property>
  11. <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
  12. <property name="hibernate.show_sql">true</property>
  13. <property name="hibernate.format_sql">true</property>
  14. <property name="current_session_context_class">thread</property>
  15. <property name="hibernate.hbm2ddl.auto">create</property>
  16. <mapping resource="com/zze/bean/Class.hbm.xml"></mapping>
  17. <mapping resource="com/zze/bean/Student.hbm.xml"></mapping>
  18. </session-factory>
  19. </hibernate-configuration>

hibernate.cfg.xml

普通操作

  1. @Test
  2. public void test1() {
  3. Session session = HibernateUtil.getCurrentSession();
  4. Transaction transaction = session.beginTransaction();
  5. // 创建一个班级
  6. Class clazz = new Class();
  7. clazz.setName("1班");
  8. // 创建两个学生
  9. Student student1 = new Student();
  10. student1.setName("张三");
  11. Student student2 = new Student();
  12. student2.setName("李四");
  13.  
  14. // 让班级关联学生
  15. clazz.getStudents().add(student1);
  16. clazz.getStudents().add(student2);
  17.  
  18. // 让学生关联班级
  19. student1.setClazz(clazz);
  20. student2.setClazz(clazz);
  21. // 保存班级
  22. session.save(clazz);
  23.  
  24. // 保存学生
  25. session.save(student1);
  26. session.save(student2);
  27. transaction.commit();
  28. }

例 1:两方关联,保存两方 - 新建一个班级,新建两个学生,班级主动关联学生,学生也主动关联班级,保存班级,再保存学生,成功

  1. @Test
  2. public void test2() {
  3. Session session = HibernateUtil.getCurrentSession();
  4. Transaction transaction = session.beginTransaction();
  5. // 创建一个班级
  6. Class clazz = new Class();
  7. clazz.setName("1班");
  8. // 创建两个学生
  9. Student student1 = new Student();
  10. student1.setName("张三");
  11. Student student2 = new Student();
  12. student2.setName("李四");
  13.  
  14. // 让班级关联学生
  15. clazz.getStudents().add(student1);
  16. clazz.getStudents().add(student2);
  17.  
  18. // 让学生关联班级
  19. student1.setClazz(clazz);
  20. student2.setClazz(clazz);
  21. // 保存班级
  22. session.save(clazz);
  23. transaction.commit();
  24.  
  25. /*
  26. 抛异常:org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.zze.bean.Student
  27. 原因是:保存班级时,班级关联的学生为瞬时态对象,不能与瞬时态对象建立关系。
  28. */
  29. }

例 2:两方关联,保存一方 - 新建一个班级,新建两个学生,班级主动关联学生,学生也主动关联班级,只保存班级,异常,需使用下面的级联保存。

级联操作

级联:

级联指的是:操作一个对象的时候,是否会同时操作其关联的对象。

级联的方向性:

操作一的一方的时候,是否会同时操作到多的一方。

操作多的一方的时候,是否会同时操作到一的一方。

  • 级联保存或更新

    保存‘一’级联保存‘多’:

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE hibernate-mapping PUBLIC
    3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    4. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    5.  
    6. <hibernate-mapping>
    7. <class name="com.zze.bean.Class" table="class">
    8. <id name="id">
    9. <generator class="native"/>
    10. </id>
    11. <property name="name" length="32"/>
    12. <set name="students" cascade="save-update">
    13. <key column="cid"/>
    14. <one-to-many class="com.zze.bean.Student"/>
    15. </set>
    16. </class>
    17. </hibernate-mapping>

    com/zze/bean/Class.hbm.xml:修改‘一’(班级)的映射文件,在 set 标签中添加属性 cascade="save-update"

    1. @Test
    2. public void test3() {
    3. Session session = HibernateUtil.getCurrentSession();
    4. Transaction transaction = session.beginTransaction();
    5. // 创建一个班级
    6. Class clazz = new Class();
    7. clazz.setName("1班");
    8. // 创建两个学生
    9. Student student1 = new Student();
    10. student1.setName("张三");
    11. Student student2 = new Student();
    12. student2.setName("李四");
    13.  
    14. // 让班级关联学生
    15. clazz.getStudents().add(student1);
    16. clazz.getStudents().add(student2);
    17. // 保存班级级联保存学生
    18. session.save(clazz);
    19. transaction.commit();
    20. }

    例 3:‘一’方关联,保存‘一’方 - 新建一个班级,新建两个学生,班级主动关联学生,只保存班级,级联保存了学生,成功

    保存‘多’级联保存‘一’:

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE hibernate-mapping PUBLIC
    3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    4. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    5.  
    6. <hibernate-mapping>
    7. <class name="com.zze.bean.Student" table="student">
    8. <id name="id">
    9. <generator class="native"/>
    10. </id>
    11. <property name="name" length="32"/>
    12. <property name="age"/>
    13. <property name="birthday"/>
    14. <property name="address"/>
    15. <many-to-one name="clazz" column="cid" class="com.zze.bean.Class" cascade="save-update"/>
    16. </class>
    17. </hibernate-mapping>

    com/zze/bean/Student.hbm.xml:修改‘多’(学生)的映射文件,在 many-to-one 标签中添加属性 cascade="save-update"

    1. @Test
    2. public void test4(){
    3. Session session = HibernateUtil.getCurrentSession();
    4. Transaction transaction = session.beginTransaction();
    5. // 创建一个班级
    6. Class clazz = new Class();
    7. clazz.setName("1班");
    8. // 创建两个学生
    9. Student student1 = new Student();
    10. student1.setName("张三");
    11. Student student2 = new Student();
    12. student2.setName("李四");
    13.  
    14. // 让学生关联班级
    15. student1.setClazz(clazz);
    16. student2.setClazz(clazz);
    17. // 保存学生级联保存班级
    18. session.save(student1);
    19. session.save(student2);
    20. transaction.commit();
    21. }

    例 4:‘多’方关联,保存‘多’方 - 新建一个班级,新建两个学生,学生主动关联班级,只保存学生,级联保存了班级,成功

  • 级联删除

    假如表中已有如下数据:

    删除‘一’级联删除‘多’:

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE hibernate-mapping PUBLIC
    3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    4. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    5.  
    6. <hibernate-mapping>
    7. <class name="com.zze.bean.Class" table="class">
    8. <id name="id">
    9. <generator class="native"/>
    10. </id>
    11. <property name="name" length="32"/>
    12. <set name="students" cascade="save-update,delete">
    13. <key column="cid"/>
    14. <one-to-many class="com.zze.bean.Student" />
    15. </set>
    16. </class>
    17. </hibernate-mapping>

    com/zze/bean/Class.hbm.xml:修改‘一’(班级)的映射文件,在 set 标签中添加属性 cascade="delete"

    1. @Test
    2. public void test5() {
    3. Session session = HibernateUtil.getCurrentSession();
    4. Transaction transaction = session.beginTransaction();
    5. Class clazz = session.get(Class.class, 1);
    6. session.delete(clazz);
    7. transaction.commit();
    8. }

    例 5:删除班级,级联删除该班级下所有学生。

    删除‘多’级联删除‘一’:

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE hibernate-mapping PUBLIC
    3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    4. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    5.  
    6. <hibernate-mapping>
    7. <class name="com.zze.bean.Student" table="student">
    8. <id name="id">
    9. <generator class="native"/>
    10. </id>
    11. <property name="name" length="32"/>
    12. <property name="age"/>
    13. <property name="birthday"/>
    14. <property name="address"/>
    15. <many-to-one name="clazz" column="cid" class="com.zze.bean.Class" cascade="save-update,delete"/>
    16. </class>
    17. </hibernate-mapping>

    com/zze/bean/Student.hbm.xml:修改‘多’(学生)的映射文件,在 many-to-one 标签中添加属性 cascade="delete"

    1. @Test
    2. public void test6() {
    3. Session session = HibernateUtil.getCurrentSession();
    4. Transaction transaction = session.beginTransaction();
    5. Student student = session.get(Student.class, 1);
    6. session.delete(student);
    7. transaction.commit();
    8. // 级联删除‘一’的一方,将其它关联‘一’的一方的数据的外键设为 null。
    9. // 如果‘一’的一方也设置了级联删除,那么所有关联‘一’的一方的数据都会被删除。
    10. }

    例 6:删除学生,级联删除该学生所属班级。

放弃维护外键关系

假入表中已有如下数据:

先要把张三从 1 班转到 2 班:

  1. @Test
  2. public void test7(){
  3. Session session = HibernateUtil.getCurrentSession();
  4. Transaction transaction = session.beginTransaction();
  5. Student student = session.get(Student.class, 1); // 获取张三
  6. Class clazz = session.get(Class.class, 1); // 获取 2 班
  7. clazz.getStudents().add(student);
  8. student.setClazz(clazz);
  9. transaction.commit();
  10. /*
  11. 此时会执行一下两条 SQL:
  12. Hibernate:
  13. update
  14. student
  15. set
  16. name=?,
  17. age=?,
  18. birthday=?,
  19. address=?,
  20. cid=?
  21. where
  22. id=?
  23. Hibernate:
  24. update
  25. student
  26. set
  27. cid=?
  28. where
  29. id=?
  30. */
  31. }

例 7:

执行两条 SQL 的原因是:在上述代码中获取了两个持久态对象,并且在事务提交时两个持久态对象的属性都发生了变化,所以 Hibernate 发出了两条 SQL。

但显然这个操作其实一条 SQL 就能搞定的,这个时候就需要控制一方不维护外键关系了。

‘一’的一方放弃维护外键关系:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
  5.  
  6. <hibernate-mapping>
  7. <class name="com.zze.bean.Class" table="class">
  8. <id name="id">
  9. <generator class="native"/>
  10. </id>
  11. <property name="name" length="32"/>
  12. <set name="students" cascade="save-update" inverse="true">
  13. <key column="cid"/>
  14. <one-to-many class="com.zze.bean.Student" />
  15. </set>
  16. </class>
  17. </hibernate-mapping>

com/zze/bean/Class.hbm.xml:修改‘一’(班级)映射文件,在 set 标签中添加属性 inverse="true"

依旧是执行上述“例 7”代码,会发现只会由‘多’(学生)的一方发出一条 update 语句。

在 Hibernate 中,‘多’的一方不可以放弃维护外键关系。

为什么‘多’的一方不能放弃维护外键关系?换句话说为什么 Hibernate 要让多的一方来维护关系?

原因是当‘一’的一方维护关系时,Hibernate 会额外发出一条 select 语句查出它所关联的所有的多的一方,显然没必要。而多的一方维护关系就比较简单了,直接 update 它本身的外键即可。

看到过一个有趣应景的说法:十三亿人民(‘多’)记住(维护)国家领导人的名字很简单,但要想国家领导人(‘一’)记住(维护)十三亿就不可能了。

理解 inverse 和 cascade:
  1. @Test
  2. public void test8() {
  3. // com/zze/bean/Class.hbm.xml 的 set 中设置了 cascade="save-update"、inverse="true"
  4. Session session = HibernateUtil.getCurrentSession();
  5. Transaction transaction = session.beginTransaction();
  6. Student student = new Student();
  7. student.setName("王五");
  8. Class clazz = session.get(Class.class, 2); // 获取 1 班
  9. clazz.getStudents().add(student);
  10. session.save(clazz);
  11. transaction.commit();
  12.  
  13. /*
  14. 上述代码执行结果会是怎么样?
  15. 先明确 cascade 和 inverse 的作用:
  16. cascade:为 save-update,即 Class 会级联保存和更新 Student。
  17. inverse:为 true,即 Class 放弃维护外键关系。
  18. 首先 session 保存的是 clazz,clazz 是持久态对象。
  19. 在事务提交时 clazz 的外键字段 students 发生了变化,添加了 student,
  20. 班级有级联操作学生的能力,所以“王五”这个 student 会被保存到数据库。
  21. 而班级放弃了维护外键关系,所以“王五”这个 student 添加到数据库后对应的外键字段为 null。
  22. 结果如下:
  23. mysql> select * from student;
  24. +----+--------+------+----------+---------+------+
  25. | id | name | age | birthday | address | cid |
  26. +----+--------+------+----------+---------+------+
  27. | 1 | 张三 | NULL | NULL | NULL | 1 |
  28. | 2 | 王五 | NULL | NULL | NULL | NULL |
  29. +----+--------+------+----------+---------+------+
  30. 2 rows in set (0.00 sec)
  31. */
  32. }

例 8:

多对多

例:一名学生可以选多门课程,一门课程也可以被多名学生选择:

模型

  1. package com.zze.bean;
  2.  
  3. import java.util.Date;
  4. import java.util.HashSet;
  5. import java.util.Set;
  6.  
  7. public class Student {
  8. private Integer id;
  9. private String name;
  10. private Integer age;
  11. private Date birthday;
  12. private String address;
  13.  
  14. private Class clazz;
  15.  
  16. private Set<Course> courses = new HashSet<>();
  17.  
  18. public Integer getId() {
  19. return id;
  20. }
  21.  
  22. public void setId(Integer id) {
  23. this.id = id;
  24. }
  25.  
  26. public String getName() {
  27. return name;
  28. }
  29.  
  30. public void setName(String name) {
  31. this.name = name;
  32. }
  33.  
  34. public Integer getAge() {
  35. return age;
  36. }
  37.  
  38. public void setAge(Integer age) {
  39. this.age = age;
  40. }
  41.  
  42. public Date getBirthday() {
  43. return birthday;
  44. }
  45.  
  46. public void setBirthday(Date birthday) {
  47. this.birthday = birthday;
  48. }
  49.  
  50. public String getAddress() {
  51. return address;
  52. }
  53.  
  54. public void setAddress(String address) {
  55. this.address = address;
  56. }
  57.  
  58. public Class getClazz() {
  59. return clazz;
  60. }
  61.  
  62. public void setClazz(Class clazz) {
  63. this.clazz = clazz;
  64. }
  65.  
  66. public Set<Course> getCourses() {
  67. return courses;
  68. }
  69.  
  70. public void setCourses(Set<Course> courses) {
  71. this.courses = courses;
  72. }
  73. }

学生:com.zze.bean.Student

  1. package com.zze.bean;
  2.  
  3. import java.util.HashSet;
  4. import java.util.Set;
  5.  
  6. public class Course {
  7. private Integer id;
  8. private String name;
  9.  
  10. private Set<Student> students = new HashSet<>();
  11.  
  12. public Integer getId() {
  13. return id;
  14. }
  15.  
  16. public void setId(Integer id) {
  17. this.id = id;
  18. }
  19.  
  20. public String getName() {
  21. return name;
  22. }
  23.  
  24. public void setName(String name) {
  25. this.name = name;
  26. }
  27.  
  28. public Set<Student> getStudents() {
  29. return students;
  30. }
  31.  
  32. public void setStudents(Set<Student> students) {
  33. this.students = students;
  34. }
  35. }

课程:com.zze.bean.Course

配置文件

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
  5.  
  6. <hibernate-mapping>
  7. <class name="com.zze.bean.Student" table="student">
  8. <id name="id">
  9. <generator class="native"/>
  10. </id>
  11. <property name="name" length="32"/>
  12. <property name="age"/>
  13. <property name="birthday"/>
  14. <property name="address"/>
  15. <many-to-one name="clazz" column="cid" class="com.zze.bean.Class" cascade="save-update,delete"/>
  16.  
  17. <set name="courses" table="student_course">
  18. <!--
  19. key :
  20. column : 当前对象类对应中间表的外键名称
  21. -->
  22. <key column="studentId"/>
  23. <!--
  24. many-to-many : 标识多对多关系
  25. class : 对方类全路径
  26. column : 对方对应中间表的外键名称
  27. -->
  28. <many-to-many class="com.zze.bean.Course" column="courseId"/>
  29. </set>
  30. </class>
  31. </hibernate-mapping>

com/zze/bean/Student.hbm.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
  5.  
  6. <hibernate-mapping>
  7. <class name="com.zze.bean.Course" table="course">
  8. <id name="id">
  9. <generator class="native"/>
  10. </id>
  11. <property name="name" length="32"/>
  12. <set name="students" table="student_course">
  13. <!--
  14. key :
  15. column : 当前对象类对应中间表的外键名称
  16. -->
  17. <key column="courseId"/>
  18. <!--
  19. many-to-many : 标识多对多关系
  20. class : 对方类全路径
  21. column : 对方对应中间表的外键名称
  22. -->
  23. <many-to-many class="com.zze.bean.Student" column="studentId"/>
  24. </set>
  25. </class>
  26. </hibernate-mapping>

com/zze/bean/Course.hbm.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE hibernate-configuration PUBLIC
  3. "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
  4. "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
  5. <hibernate-configuration>
  6. <session-factory>
  7. <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
  8. <property name="hibernate.connection.url">jdbc:mysql://192.168.208.153:3306/test</property>
  9. <property name="hibernate.connection.username">root</property>
  10. <property name="hibernate.connection.password">root</property>
  11. <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
  12. <property name="hibernate.show_sql">true</property>
  13. <property name="hibernate.format_sql">true</property>
  14. <property name="current_session_context_class">thread</property>
  15. <property name="hibernate.hbm2ddl.auto">create</property>
  16. <mapping resource="com/zze/bean/Class.hbm.xml"></mapping>
  17. <mapping resource="com/zze/bean/Student.hbm.xml"></mapping>
  18. <mapping resource="com/zze/bean/Course.hbm.xml"></mapping>
  19. </session-factory>
  20. </hibernate-configuration>

hibernate.cfg.xml

普通操作

  1. @Test
  2. public void test9() {
  3. Session session = HibernateUtil.getCurrentSession();
  4. Transaction transaction = session.beginTransaction();
  5. Student student = new Student();
  6. student.setName("张三");
  7. Course course = new Course();
  8. course.setName("数学");
  9. student.getCourses().add(course);
  10. course.getStudents().add(student);
  11. session.save(student);
  12. session.save(course);
  13. transaction.commit();
  14. /*
  15. 抛异常:org.hibernate.exception.ConstraintViolationException: could not execute statement
  16. 原因:因为多对多关系的维护是依赖第三张中间表,而中间表的字段是两个分别关联多对多这两张表的主键的外键,
  17. 且这两个外键在中间表中组成了联合主键,而上述代码实际上进行了两次 insert 操作,这两次操作的内容违
  18. 反了联合主键约束,所以会抛出异常。
  19. */
  20. }

例 9:两方关联,保存两方 - 新建一个学生,新建一个课程,让学生主动关联课程,也让课程主动关联学生,保存学生与课程,异常

分析原因后,可以得出结论,在多对多中两方关联时,必须有一方得放弃维护外键关系(一般是被动方放弃,即 Course 放弃),下面配置二选一:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
  5.  
  6. <hibernate-mapping>
  7. <class name="com.zze.bean.Student" table="student">
  8. <id name="id">
  9. <generator class="native"/>
  10. </id>
  11. <property name="name" length="32"/>
  12. <property name="age"/>
  13. <property name="birthday"/>
  14. <property name="address"/>
  15. <many-to-one name="clazz" column="cid" class="com.zze.bean.Class" cascade="save-update,delete"/>
  16.  
  17. <set name="courses" table="student_course" inverse="true">
  18. <!--
  19. key :
  20. column : 当前对象类对应中间表的外键名称
  21. -->
  22. <key column="studentId"/>
  23. <!--
  24. many-to-many : 标识多对多关系
  25. class : 对方类全路径
  26. column : 对方对应中间表的外键名称
  27. -->
  28. <many-to-many class="com.zze.bean.Course" column="courseId"/>
  29. </set>
  30. </class>
  31. </hibernate-mapping>

com/zze/bean/Student.hbm.xml:修改 set 标签,添加属性 inverse="true"

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE hibernate-mapping PUBLIC
  3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  4. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
  5.  
  6. <hibernate-mapping>
  7. <class name="com.zze.bean.Course" table="course">
  8. <id name="id">
  9. <generator class="native"/>
  10. </id>
  11. <property name="name" length="32"/>
  12. <set name="students" table="student_course" inverse="true">
  13. <!--
  14. key :
  15. column : 当前对象类对应中间表的外键名称
  16. -->
  17. <key column="courseId"/>
  18. <!--
  19. many-to-many : 标识多对多关系
  20. class : 对方类全路径
  21. column : 对方对应中间表的外键名称
  22. -->
  23. <many-to-many class="com.zze.bean.Student" column="studentId"/>
  24. </set>
  25. </class>
  26. </hibernate-mapping>

com/zze/bean/Course.hbm.xml:修改 set 标签,添加属性 inverse="true"

修改配置后,再次执行 “例 9” 中代码,成功。

级联操作

  • 级联保存或更新

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE hibernate-mapping PUBLIC
    3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    4. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    5.  
    6. <hibernate-mapping>
    7. <class name="com.zze.bean.Student" table="student">
    8. <id name="id">
    9. <generator class="native"/>
    10. </id>
    11. <property name="name" length="32"/>
    12. <property name="age"/>
    13. <property name="birthday"/>
    14. <property name="address"/>
    15. <many-to-one name="clazz" column="cid" class="com.zze.bean.Class" cascade="save-update,delete"/>
    16.  
    17. <set name="courses" table="student_course" cascade="save-update">
    18. <!--
    19. key :
    20. column : 当前对象类对应中间表的外键名称
    21. -->
    22. <key column="studentId"/>
    23. <!--
    24. many-to-many : 标识多对多关系
    25. class : 对方类全路径
    26. column : 对方对应中间表的外键名称
    27. -->
    28. <many-to-many class="com.zze.bean.Course" column="courseId"/>
    29. </set>
    30. </class>
    31. </hibernate-mapping>

    com/zze/bean/Student.hbm.xml:修改 set 标签,添加属性 cascade="save-update"

    1. @Test
    2. public void test10() {
    3. Session session = HibernateUtil.getCurrentSession();
    4. Transaction transaction = session.beginTransaction();
    5. Student student = new Student();
    6. student.setName("张三");
    7. Course course = new Course();
    8. course.setName("数学");
    9. student.getCourses().add(course);
    10. session.save(student);
    11. transaction.commit();
    12. }

    例 10:一方关联,保存一方 - 新建一个学生,新建一个课程,让学生主动关联课程,保存学生,级联保存了课程,成功

  • 级联删除

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE hibernate-mapping PUBLIC
    3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    4. "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    5.  
    6. <hibernate-mapping>
    7. <class name="com.zze.bean.Student" table="student">
    8. <id name="id">
    9. <generator class="native"/>
    10. </id>
    11. <property name="name" length="32"/>
    12. <property name="age"/>
    13. <property name="birthday"/>
    14. <property name="address"/>
    15. <many-to-one name="clazz" column="cid" class="com.zze.bean.Class" cascade="save-update,delete"/>
    16.  
    17. <set name="courses" table="student_course" cascade="save-update,delete">
    18. <!--
    19. key :
    20. column : 当前对象类对应中间表的外键名称
    21. -->
    22. <key column="studentId"/>
    23. <!--
    24. many-to-many : 标识多对多关系
    25. class : 对方类全路径
    26. column : 对方对应中间表的外键名称
    27. -->
    28. <many-to-many class="com.zze.bean.Course" column="courseId"/>
    29. </set>
    30. </class>
    31. </hibernate-mapping>

    com/zze/bean/Student.hbm.xml:修改 set 标签,添加属性 cascade="delete"

    1. @Test
    2. public void test11() {
    3. Session session = HibernateUtil.getCurrentSession();
    4. Transaction transaction = session.beginTransaction();
    5. Student student = session.get(Student.class, 1);
    6. session.delete(student);
    7. transaction.commit();
    8. }

    例 11:删除学生,级联删除该学生所选的课程

java框架之Hibernate(3)-一对多和多对多关系操作的更多相关文章

  1. Java基础-SSM之mybatis一对多和多对一关系映射

    Java基础-SSM之mybatis一对多和多对一关系映射 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.准备测试环境(创建数据库表)  1>.创建customers表: ...

  2. Hibernate中一对多和多对一关系

    1.单向多对一和双向多对一的区别? 只需要从一方获取另一方的数据时 就使用单向关联双方都需要获取对方数据时 就使用双向关系 部门--人员 使用人员时如果只需要获取对应部门信息(user.getdept ...

  3. Hibernate自身一对多和多对多关系映射

    一对多关系映射大家都明白,关系双方都一个含有对方多个引用,但自身一对多很多同学都不明白什么意思,那么首先我就说明一下什么是自身一对多,其实也很好理解,自身一对多就是自身含有本身的多个引用,例如新闻类别 ...

  4. JAVA框架之Hibernate框架的学习步骤

    首先介绍一下Java三大框架的关系 以CRM项目即客户关系管理项目示例 hibernate框架的学习路线: 1.学习框架入门,自己搭建框架,完成增删改查的操作 2.学习一级缓存,事物管理和基本查询 3 ...

  5. JPA一对多和多对一关系

    1-m:多的一方为关系维护端,关系维护端负责外键纪录的更新,关系被维护端没有权力更新外键纪录. 维护端注解 @OneToMany(cascade = { CascadeType.PERSIST, Ca ...

  6. EF里一对一、一对多、多对多关系的配置和级联删除

    本章节开始了解EF的各种关系.如果你对EF里实体间的各种关系还不是很熟悉,可以看看我的思路,能帮你更快的理解. I.实体间一对一的关系 添加一个PersonPhoto类,表示用户照片类 /// < ...

  7. Django 一对多,多对多关系解析

    [转]Django 一对多,多对多关系解析   Django 的 ORM 有多种关系:一对一,多对一,多对多. 各自定义的方式为 :        一对一: OneToOneField         ...

  8. EF——一对一、一对多、多对多关系的配置和级联删除 04(转)

    EF里一对一.一对多.多对多关系的配置和级联删除   本章节开始了解EF的各种关系.如果你对EF里实体间的各种关系还不是很熟悉,可以看看我的思路,能帮你更快的理解. I.实体间一对一的关系 添加一个P ...

  9. django ORM模型表的一对多、多对多关系、万能双下划线查询

    一.外键使用 在 MySQL 中,如果使用InnoDB引擎,则支持外键约束.(另一种常用的MyIsam引擎不支持外键) 定义外键的语法为fieldname=models.ForeignKey(to_c ...

随机推荐

  1. Cordova 项目 加载不出XML文件

    解决方法:copy bundle 将文件移除再添加

  2. Java JDK下载、安装与环境变量配置

    https://blog.csdn.net/siwuxie095/article/details/53386227 https://blog.csdn.net/liudongdong19/articl ...

  3. Ajax 请求头中常见content-type

    四种常见的 POST 提交数据方式 HTTP 协议是以 ASCII 码传输,建立在 TCP/IP 协议之上的应用层规范.规范把 HTTP 请求分为三个部分:状态行.请求头.消息主体.协议规定 POST ...

  4. Windows上使用Vagrant打造Laravel Homestead可协同跨平台开发环境

    1.简介 Laravel 致力于让整个 PHP 开发过程变得让人愉悦,包括本地开发环境,为此官方为我们提供了一整套本地开发环境 —— Laravel Homestead. Laravel Homest ...

  5. MYSQL数据库高可用方案探究

    MySQL作为最关键的应用数据存储中心,如何保证MySQL服务的可靠性和持续性,是我们不得不细致考虑的一个问题.当master宕机的时候,我们如何保证数据尽可能的不丢失,如何保证快速的获知master ...

  6. 【Clojure 基本知识】 ns宏的 指令(关键字) requrie的用法

    指令(:require)用在(ns)之中,下面是实践中总结的几种用法(下文中省略ns宏,只是给出:require的代码): 一.导入完整名称空间. 1,最简单的形式: (:require clojur ...

  7. Oracle误删除数据的恢复方法(转)

    来源:原创网站北京北亚数据恢复中心,转载须注明出处. 学习数据库时,我们只是以学习的态度,考虑如何使用数据库命令语句,并未想过工作中,如果误操作一下,都可能导致无可挽回的损失.当我在工作中真正遇到这些 ...

  8. [python]socket.listen(backlog)中的backlog含义

    http://www.nosa.me/2015/09/16/socket-listenbacklog-%E4%B8%AD-backlog-%E6%8C%87%E7%9A%84%E6%98%AF%E4% ...

  9. 六种常见排序算法的java实现

    package edu.cn.ysw; //八种排序算法的实现与效率分析 /* * 内排序的种类: * 1.插入排序:直接插入排序.希尔排序. * 2.选择排序:简单选择排序.堆排序. 3.交换排序: ...

  10. nw.js---创建一个hello word的方法

    一.如果用nw.js 来开发桌面应用 首先到Nw.js中文网下载软件: https://nwjs.org.cn/download.html 下载下来进行解压就可以了,绿色的免安装的,整个目录结果是这样 ...