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
 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
getSchoolStudents() {        return schoolStudents;    }    publicvoid setSchoolStudents(Set
 schoolStudents) {        this.schoolStudents = schoolStudents;    }}

School.hbm.xml:

    
        
            
                 
        
            
            
            

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:

    
        
            
                 
        
    

1.  Cascade

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

    ①   没有配置cascade的情况

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

@Testpublic 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里面的配置为:

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

    School表:

    wKiom1bStYzQKyASAAAJ7v0fm58539.png

    Student表:

    wKioL1bStgDi1wkRAAAPcZQT1uk702.png

    同样,我们也可以吧student1student2student3存入school1和school2,最后保存school1和

    school2。只要把School.hbm.xml里面的配置改为:

    
    

    测试方法如下:

@Testpublicvoid 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
 students1 = newHashSet
();    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
 students2 = newHashSet
();    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的配置改为:

    
    

   表示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会出错。