Hibernate的inverse和cascade都含有维护相互关系的意思。根据网上的解释cascade表示级联关系,而inverse表示由谁来维护关系。只从字面上不是很好理解,所以下面结合实例来说明下inverse和cascade的区别,简单起见只用双向多对一来说明,下面是本例用到的两张表。
School表结构:
对应的实体类:
package com.jaeger.hibernate.test; import java.util.Set; publicclass School { private int schoolId; private String schoolName; private SetschoolStudents; 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为外键):
对应的实体类:
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表:
Student表:
同样,我们也可以吧student1、student2、student3存入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("小岛秀夫"); Setstudents1 = 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表:
Student表:
此时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表:
Student表:
此时只保存了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会出错。