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 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为外键):
对应的实体类:
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表:
Student表:
同样,我们也可以吧student1、student2、student3存入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表:
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会出错。
转载于:https://blog.51cto.com/jaeger/1745443