Hibernate的inverse和cascade都含有维护相互关系的意思。根据网上的解释cascade表示级联关系,而inverse表示由谁来维护关系。只从字面上不是很好理解,所以下面结合实例来说明下inverse和cascade的区别,简单起见只用双向多对一来说明,下面是本例用到的两张表。

School表结构:

wKioL1bSs23C2u9gAAAIM4pTF18624.png

对应的实体类:

package com.jaeger.hibernate.test;
 
import java.util.Set;
 
publicclass School {
    private int schoolId;
    private String schoolName;
    private Set<Student> schoolStudents;
    publicint getSchoolId() {
        return schoolId;
    }
    publicvoid setSchoolId(intschoolId) {
        this.schoolId = schoolId;
    }
    public String getSchoolName() {
        return schoolName;
    }
    publicvoid setSchoolName(String schoolName) {
        this.schoolName = schoolName;
    }
    public Set<Student>getSchoolStudents() {
        return schoolStudents;
    }
    publicvoid setSchoolStudents(Set<Student> schoolStudents) {
        this.schoolStudents = schoolStudents;
    }
}

School.hbm.xml:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/HibernateMapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.jaeger.hibernate.test.School" table="school">
        <id name="schoolId" column="school_id">
            <generator class="native"></generator>
        </id>
 
        <property name="schoolName" column="school_name" />
        <set name="schoolStudents">
            <key column="student_school_id"></key>
            <one-to-many class="com.jaeger.hibernate.test.Student"/>
        </set>
    </class>
</hibernate-mapping>

Student表结构(student_school_id为外键):

wKioL1bStBbQtmuSAAAH1gGY_mY155.png

对应的实体类:

package com.jaeger.hibernate.test;
 
publicclass Student {
    private int studentId;
    private String studentName;
    private School studentSchool;
    publicint getStudentId() {
        return studentId;
    }
    publicvoid setStudentId(intstudentId) {
        this.studentId = studentId;
    }
    public String getStudentName() {
        return studentName;
    }
    publicvoid setStudentName(String studentName) {
        this.studentName = studentName;
    }
    public School getStudentSchool() {
        return studentSchool;
    }
    publicvoid setStudentSchool(School studentSchool) {
        this.studentSchool = studentSchool;
    }
}

Student.hbm.xml:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/HibernateMapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.jaeger.hibernate.test.Student" table="student">
        <id name="studentId" column="student_id">
            <generator class="native"></generator>
        </id>
 
        <property name="studentName" column="student_name" />
        <many-to-one name="studentSchool" class="com.jaeger.hibernate.test.School" 
           foreign-key="fk_student_school" column="student_school_id"></many-to-one>
    </class>
</hibernate-mapping>

1.  Cascade

    Cascade有很多取值,此处使用最常用的save-update进行说明。

    ①   没有配置cascade的情况

        在Student.hbm.xml和School.hbm.xml里面我们都没有配置cascade。

@Test
public void createDataFromManySide(){
     Configuration config = new Configuration()
         .configure("com/jaeger/hibernate/test/hibernate.cfg.xml");
     ServiceRegistry sr = newStandardServiceRegistryBuilder()
         .applySettings(config.getProperties()).build();
     SessionFactory sf = config.buildSessionFactory(sr);
     Session session = sf.openSession();
     Transaction transaction = session.beginTransaction();
        
     School school1 = new School();
     school1.setSchoolName("KONAMI");
     Student student1 = new Student();
     student1.setStudentName("小岛秀夫");
     student1.setStudentSchool(school1);
     
     School school2 = new School();
     school2.setSchoolName("NINTENDO");
     Student student2 = new Student();
     student2.setStudentName("岩田聪");
     student2.setStudentSchool(school2);
     Student student3 = new Student();
     student3.setStudentName("宫本茂");
     student3.setStudentSchool(school2);
        
     session.save(student1); //⑴
     session.save(student2); //⑵
     session.save(student3); //⑶
        
     transaction.commit();
     session.close();
}

上面例子中student1、student2、student3里面含有school1和school2,但我们如果只save 3个
    student的时候,hibernate会报错,告诉我们必须先保存school1和school2。

    org.hibernate.TransientObjectException:object references an unsaved transient instance
    - save the transient instancebefore flushing: com.jaeger.hibernate.test.School
    ②  把cascade设置为save-update

    修改Student.hbm.xml里面的配置为:

<many-to-one name="studentSchool" class="com.jaeger.hibernate.test.School" 
    foreign-key="fk_student_school"column="student_school_id" cascade="save-update">
</many-to-one>

    这个时候我们再去save 3个student就没有任何问题了,结果如下:

    School表:

    wKiom1bStYzQKyASAAAJ7v0fm58539.png

    Student表:

    wKioL1bStgDi1wkRAAAPcZQT1uk702.png

    同样,我们也可以吧student1student2student3存入school1和school2,最后保存school1和
    school2。只要把School.hbm.xml里面的配置改为:

<set name="schoolStudents" cascade="save-update">
    <key column="student_school_id"></key>
    <one-to-many class="com.jaeger.hibernate.test.Student" />
</set>

    测试方法如下:

@Test
publicvoid createDataFromOneSide(){
    Configuration config = new Configuration()
        .configure("com/jaeger/hibernate/test/hibernate.cfg.xml");
    ServiceRegistry sr = newStandardServiceRegistryBuilder()
        .applySettings(config.getProperties()).build();
    SessionFactory sf = config.buildSessionFactory(sr);
    Session session = sf.openSession();
    Transaction transaction = session.beginTransaction();
    
    Student student1 = new Student();
    student1.setStudentName("小岛秀夫");
    Set<Student> students1 = newHashSet<Student>();
    students1.add(student1);
    School school1 = new School();
    school1.setSchoolName("KONAMI");
    school1.setSchoolStudents(students1);
        
        
    Student student2 = new Student();
    student2.setStudentName("岩田聪");
    Student student3 = new Student();
    student3.setStudentName("宫本茂");
    Set<Student> students2 = newHashSet<Student>();
    students2.add(student2);
    students2.add(student3);
    School school2 = new School();
    school2.setSchoolName("NINTENDO");
    school2.setSchoolStudents(students2);
        
    session.save(school1);
    session.save(school2);
        
    transaction.commit();
    session.close();
}

2. Inverse

   inverse用来指明由哪方来维护引用关系,如果inverse="true"表示让出维护关系的权力,让对方来
   维护关系,而inverse="false"表示本方也需要维护关系。<many-to-one></many-to-one>标签不可以
   设置invserse属性,它默认自带inverse="false"。

   inverse一般用于<one-to-many>标签,所以下面就用<one-to-many>标签来说明。把上面
   School.hbm.xml的配置改为:

<set name="schoolStudents" cascade="save-update" inverse="true">
    <key column="student_school_id"></key>
    <one-to-many class="com.jaeger.hibernate.test.Student" />
</set>

   表示school类不再维护与student之间的关系,这样说比较抽象。我们再次运行上面
   createDataFromOneSide()测试方法,运行结果如下:

   School表:

   wKioL1bSuBKjO4vfAAAJkeGghrA376.png

   Student表:

   wKiom1bSt56zezspAAATDAJpxDs055.png

   此时Student表的外键为空,因为School类已经声明为不维护与Student的关系,虽然Student里面创
   建了记录,但不会保存指向School的外键。

3.  <one-to-many>中inverse和cascade其他组合的效果

    ①   cascade="save-update" inverse="false"

    <one-to-many>中如果不配置inverse,则默认为inverse="false"。这种配置可以从通过School来创
    建Student,并添加Student的外键。

    ②   只配inverse="false"

    这种配置会导致save出错,必须要手动先save Student,因为不允许save School的时候创建
    Student。

    ③   只配inverse="true"

    运行结果如下:

    School表:

    wKioL1bSuJCjGqaQAAAJpfs2TDs155.png

    Student表:

    wKiom1bSuByhVaxSAAAMEoJ0YHY827.png

    此时只保存了School的数据,因为虽然不允许save School的时候创建Student,但School也不需要
    维护与Student的关系,所以只保存School的数据即可。

4.  从one维护关系和many端维护关系的不同

    我们从Hibernate生成的sql来看看,两种关系维护方法的不同。我们还是用上面的Student和School
    的例子来说明。

    ①   把School放入Student,然后save Student,生成的sql如下:

Hibernate:
   insert
   into
       school
       (school_name)
   values
       (?)
Hibernate:
   insert
   into
       student
       (student_name, student_school_id)
   values
       (?, ?)

   ②   把Student放入School,然后save School,生成的sql如下:

Hibernate:
    insert
    into
        school
        (school_name)
    values
        (?)
Hibernate:
    insert
    into
        student
        (student_name, student_school_id)
    values
        (?, ?)
Hibernate:
    update
        student
    set
        student_school_id=?
    where
        student_id=?

    可以看到从many端来维护关系的话,Hibernate会先插入School和Student数据,但Student里面的外
    键为null,最后在update Student的外键。相对于从one端维护many端来维护的话会多一个update语
    句,如果数据量很大,会多出很多不必要的update操作。

    而且这种方法要求Student表的外键不能是not-null的,否则开始的insert会出错。