Hibernate关系映射-级联操作


Hibernate关系映射-级联操作cascade

在双向一对多的学习中,我们发现每次保存对象时,学生对象和年纪对象都需要我们持久化之session,既然它们两者有关联关系,可不可以只持久化其中一端,另一端就会自动的被持久化呢,这个属性就是Hibernate的cascade。cascade是多对一、一对多、一对一、多对多各种关系之间的一种级联操作行为。(关键语句:cascade="save-update"。)

一、什么是级联操作

官方解释:必须级联到关联目标的操作,默认情况下没有级联操作。

当Hibernate持久化一个瞬时对象时,在默认情况下,它不会自动持久化所关联的其他临时对象,而是会抛出org.hibernate.TransientObjectException。

级联的意思是:本实体做了什么事,也要拉上另一个关联的实体,导致另一个实体跟着做事情。就是说我删除了,你也必须删除!关联目标,指的是关联的那个实体 。

二、准备案例

在明确了级联操作的概念知识以后,使用学生表和年纪表的双向一对多关系,实现级联操作行为。

学生表和年纪表的双向一对多关系学习请参考(Hibernate关系映射-双向一对多)。

cascade级联操作,默认是none不级联。

none:不级联。

save-update:保存和更新某一端数据时,另一端数据可以一起保存和更新。

delete:删除级联-不能在多的一端使用。

all:表示所有操作都级联

三、实现级联操作

1、配置多端

  1. 编写配置文件

    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 package="com.uestc">
        <class name="Student">
            <id name="id">
                <generator class="native"/>
            id>
            <property name="name"/>
            <property name="age"/>
            
            <many-to-one cascade="save-update" name="grade" class="Grade"
                         column="grade_id" foreign-key="fk_grade"/>
        class>
    hibernate-mapping>

    2.编写Grade.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 package="com.uestc">
        <class name="Grade">
            <id name="id">
                <generator class="native"/>
            id>
            <property name="name"/>
            
            <set name="students" cascade="save-update">
                <key foreign-key="fk_grade" column="grade_id">key>
                <one-to-many class="Student"/>
            set>
        class>
    hibernate-mapping>
    1. Student类

    public class Student implements Serializable {
        private Integer id;
        private String name;
        private Integer age;
        private Grade grade;
    ?
        @Override
        public String toString() {
            return "Student{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", age=" + age +
                    ", grade=" + grade +
                    '}';
        }
    ?
        public Integer getId() {
            return id;
        }
    ?
        public void setId(Integer id) {
            this.id = id;
        }
    ?
        public String getName() {
            return name;
        }
    ?
        public void setName(String name) {
            this.name = name;
        }
    ?
        public Integer getAge() {
            return age;
        }
    ?
        public void setAge(Integer age) {
            this.age = age;
        }
    ?
        public Grade getGrade() {
            return grade;
        }
    ?
        public void setGrade(Grade grade) {
            this.grade = grade;
        }
    } 
    1. Grade类

      public class Grade implements Serializable {
          private Integer id;
          private String name;
          private Set students = new HashSet<>();
      ?
          @Override
          public String toString() {
              return "Grade{" +
                      "id=" + id +
                      ", name='" + name + '\'' +
                      ", students=" + students +
                      '}';
          }
      ?
          public Integer getId() {
              return id;
          }
      ?
          public void setId(Integer id) {
              this.id = id;
          }
      ?
          public String getName() {
              return name;
          }
      ?
          public void setName(String name) {
              this.name = name;
          }
      ?
          public Set getStudents() {
              return students;
          }
      ?
          public void setStudents(Set students) {
              this.students = students;
          }
      }
      1. 编写hibernate.cfg.xml

        <?xml version="1.0" encoding="utf-8" ?>
        DOCTYPE hibernate-configuration PUBLIC
              "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
              "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
        ?
        <hibernate-configuration>
           <session-factory>
              
              <property name="connection.driver_class">com.mysql.jdbc.Driverproperty>
              <property name="connection.url">jdbc:mysql:///hibernateproperty>
              <property name="connection.username">rootproperty>
              <property name="connection.password">rootproperty>
              
              <property name="dialect">org.hibernate.dialect.MySQL8Dialectproperty>
              
              <property name="hbm2ddl.auto">updateproperty>  
              
              <property name="show_sql">trueproperty>
              
              <property name="format_sql">trueproperty>
              
              <mapping resource="com/uestc/Grade.hbm.xml"/>
              <mapping resource="com/uestc/Student.hbm.xml"/>
           session-factory>
        hibernate-configuration>
        1. 测试类

          /**
               * 单向多对一
               */
              @Test
              public void testSingleManyToOne() {
                  Session session = null;
                  Transaction tx = null;
                  try {
                      session = HibernateUtil.getSession();
                      tx = session.beginTransaction();
          ?
                      //创建实例对象
                      Grade grade1 = new Grade();
                      grade1.setName("基础");
          ?
                      Grade grade2 = new Grade();
                      grade2.setName("中级");
          ?
          ?
          ?
                      Student student = new Student();
                      student.setName("张三");
                      student.setAge(18);
                      student.setGrade(grade1);
          ?
                      Student student2 = new Student();
                      student2.setName("李四");
                      student2.setAge(18);
                      student2.setGrade(grade1);
          ?
                      Student student3 = new Student();
                      student3.setName("王五");
                      student3.setAge(18);
                      student3.setGrade(grade2);
          ?
                      //存储的顺序是根据外键约束而定的,如果外键不可以为空,必须先存储外键的一端
                      //如果外键可以为空,随意存储,但是建议先存储外键的一端,因为会多执行update
          //            session.save(grade1);
          //            session.save(grade2);
          ?
                      session.save(student);
                      session.save(student2);
                      session.save(student3);
          ?
          ?
          ?
                      tx.commit();
          ?
                  }catch (Exception e){
                      e.printStackTrace();
                      tx.rollback();
                  }finally {
                      HibernateUtil.closeSession();
                  }
              }
          ?
              /**
               * 获取
               * 查询不用开启事务,会降低性能
               */
              @Test
              public void testSingleGetManyToOne() {
                  Session session = null;
                  Transaction tx = null;
                  try {
                      session = HibernateUtil.getSession();
                      tx = session.beginTransaction();
          ?
                      Student student = session.get(Student.class,1);
                      System.out.println("stuName:" +student.getName() + ",grade:" +student.getGrade().getName());
          ?
          ?
                      tx.commit();
          ?
                  }catch (Exception e){
                      e.printStackTrace();
                      tx.rollback();
                  }finally {
                      HibernateUtil.closeSession();
                  }
              }
          ?
              /**
               * 单向一对多
               */
              @Test
              public void testSingleOneToMany() {
                  Session session = null;
                  Transaction tx = null;
                  try {
                      session = HibernateUtil.getSession();
                      tx = session.beginTransaction();
          ?
                      //创建实例对象
          ?
                      Student student = new Student();
                      student.setName("张三");
                      student.setAge(18);
          ?
                      Student student2 = new Student();
                      student2.setName("李四");
                      student2.setAge(18);
          ?
          ?
                      Student student3 = new Student();
                      student3.setName("王五");
                      student3.setAge(18);
          ?
                      Grade grade1 = new Grade();
                      grade1.setName("基础");
                      grade1.getStudents().add(student);
                      grade1.getStudents().add(student2);
          ?
                      Grade grade2 = new Grade();
                      grade2.setName("中级");
                      grade2.getStudents().add(student3);
          ?
                      //存储的顺序是根据外键约束而定的,如果外键不可以为空,必须先存储外键的一端
                      //单向一对多会多执行update语句,效率不如多对一
                      session.save(grade1);
                      session.save(grade2);
          ?
          //            session.save(student);
          //            session.save(student2);
          //            session.save(student3);
          ?
          ?
                      tx.commit();
          ?
                  }catch (Exception e){
                      e.printStackTrace();
                      tx.rollback();
                  }finally {
                      HibernateUtil.closeSession();
                  }
              }
          ?
              /**
               * 获取
               * 查询不用开启事务,会降低性能
               */
              @Test
              public void testSingleGetOneToMany() {
                  Session session = null;
                  Transaction tx = null;
                  try {
                      session = HibernateUtil.getSession();
                      tx = session.beginTransaction();
          ?
                      Student student = session.get(Student.class,1);
                      System.out.println("stuName:" +student.getName() + ",grade:" +student.getGrade().getName());
                      System.out.println("---------------------分割线-------------------");
                      Grade grade = session.get(Grade.class,1);
                      System.out.println("grade:" +grade.getName());
                      for(Student stu : grade.getStudents()){
                          System.out.println("stuName:" +stu.getName());
                      }
          ?
          ?
          ?
                      tx.commit();
          ?
                  }catch (Exception e){
                      e.printStackTrace();
                      tx.rollback();
                  }finally {
                      HibernateUtil.closeSession();
                  }
          ?
              }

四、总结

大家可以尝试除了新增以外的其他操作,可以得出以下结论

  • 在多对一的关系中,多的一端不能操作级联为delete,一般在多的一端设为save-update;

  • 在一对多的关系中,如果一的一端设为delete相关配置时,多的一端不能指明外键为非空。