1、Inverse在一对多关系和多对多关系中被声明使用(在多对一中没有inverse关键字) 理解关键字:关系
"inverse",应该改成“relationship owner"吗?
在hibernate,只有“关系的拥有者”才能维护两个实体类之间的关联关系(一对多或多对多)。“inverse”关键字创建的目的是指明哪一边(实体类)是关系的拥有者。 然而“inverse”关键字生涩难懂,建议改为“relationship_owner”
总之,inverse=“true”说明当前配置文件对应的实体类不是关系的拥有者,而其对应的关联实体类是关系的拥有者(也就是inverse属性所在元素对应的集合变量引用的实体类为关系的拥有者),反之相反。
2、cascade属性的作用是描述关联对象进行操作时的级联特性。因此,只有涉及到关系的元素才有cascade属性。 理解关键字:级联
3、上示例代码:
背景描述:城市、学校、学生三个对象;全部是1对多的关系;即
TestCity.java
package com.test;
import java.util.HashSet;
import java.util.Set;
/**
* 测试-城市
* Created by zmf on 16/4/13.
*/
public class TestCity {
private int id;
private String name;
private Set<TestSchool> schoolSet = new HashSet<TestSchool>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<TestSchool> getSchoolSet() {
return schoolSet;
}
public void setSchoolSet(Set<TestSchool> schoolSet) {
this.schoolSet = schoolSet;
}
}
TestSchool.java
package com.test;
import java.util.HashSet;
import java.util.Set;
/**
* 测试-学校
* Created by zmf on 16/4/13.
*/
public class TestSchool {
private int id;
private String name;
private Set<TestStudent> students = new HashSet<TestStudent>();
private TestCity city;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<TestStudent> getStudents() {
return students;
}
public void setStudents(Set<TestStudent> students) {
this.students = students;
}
public TestCity getCity() {
return city;
}
public void setCity(TestCity city) {
this.city = city;
}
}
TestStudent.java
package com.test;
import java.util.HashSet;
/**
* 测试-学生
* Created by zmf on 16/4/13.
*/
public class TestStudent {
private int id;
private String name;
private TestSchool school;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public TestSchool getSchool() {
return school;
}
public void setSchool(TestSchool school) {
this.school = school;
}
}
映射文件:
TestCity.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse - Hibernate Tools
-->
<hibernate-mapping>
<class name="com.test.TestCity">
<id name="id">
<generator class="identity" />
</id>
<property name="name"/>
<set name="schoolSet">
<key column="city_id"></key>
<one-to-many class="com.test.TestSchool"></one-to-many>
</set>
</class>
</hibernate-mapping>
TestSchool.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse - Hibernate Tools
-->
<hibernate-mapping>
<class name="com.test.TestSchool">
<id name="id">
<generator class="identity" />
</id>
<property name="name"/>
<many-to-one name="city" column="city_id" class="com.test.TestCity"></many-to-one>
<set name="students">
<key column="school_id"></key>
<one-to-many class="com.test.TestStudent"></one-to-many>
</set>
</class>
</hibernate-mapping>
TestStudent.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse - Hibernate Tools
-->
<hibernate-mapping>
<class name="com.test.TestStudent">
<id name="id">
<generator class="identity" />
</id>
<property name="name"/>
<many-to-one name="school" column="school_id" class="com.test.TestSchool"></many-to-one>
</class>
</hibernate-mapping>
测试代码
package com.test;
import com.util.HibernateSessionFactory;
public class TestInverseAndCascade {
public static void main(String[] args) {
//testInverse_false();
//两种删除方式
TestCity city = (TestCity) HibernateSessionFactory.get(TestCity.class, 21);
// TestCity city = new TestCity();
// city.setId(19);
// //这时,city中的集合为空,则不会删除集合中的对象;
HibernateSessionFactory.delete(city);
}
/**
* 测试inverse属性为false时的关系维护,以及SQL的执行; 默认为false.
*/
public static void testInverse_false () {
TestCity city = new TestCity();
city.setName("北京");
TestSchool school = new TestSchool();
school.setName("北京一中");
TestStudent student1 = new TestStudent();
student1.setName("张三");
TestStudent student2 = new TestStudent();
student2.setName("李四");
//在主控方来设置关联关系
//城市和学校
city.getSchoolSet().add(school);
//学校和学生
school.getStudents().add(student1);
school.getStudents().add(student2);
//只处理城市
HibernateSessionFactory.add(city);
/*
1 / 没有设置cascade时,sql如下,且报错
Hibernate: insert into TestCity (name) values (?)
Hibernate: update TestSchool set city_id=? where id=?
由此可见,inverse只是负责维护之间的关系,并不负责处理save/update/delete关联对象(由cascade控制)
此处有2个解决办法,第一个是在主控方来设置cascade;第二种是自己保存城市和学生,然后由inverse来控制关系
第一种办法,先设置cascade为all,所有情况下均执行;
cascade属性可选值:
all : 所有情况下均进行关联操作。
none:所有情况下均不进行关联操作。(默认值)
save-update:在执行save/update/saveOrUpdate时进行关联操作。
delete:在执行delete时进行关联操作。
设置好,执行;
Hibernate: insert into TestCity (name) values (?)
Hibernate: insert into TestSchool (name, city_id) values (?, ?)
Hibernate: update TestSchool set city_id=? where id=?
Hibernate: update TestStudent set school_id=? where id=? //学校的学生set节点也要加
设置好,执行:
Hibernate: insert into TestCity (name) values (?)
Hibernate: insert into TestSchool (name, city_id) values (?, ?)
Hibernate: insert into TestStudent (name, school_id) values (?, ?)
Hibernate: insert into TestStudent (name, school_id) values (?, ?)
Hibernate: update TestSchool set city_id=? where id=?
Hibernate: update TestStudent set school_id=? where id=?
Hibernate: update TestStudent set school_id=? where id=?
**cascade级联先发生,最后hibernate会根据inverse来判断是否来控制关联关系!
**cascade用于触发增删改的级联操作,inverse来控制关联关系;
设计时,要根据实际情况:
1/城市的建立,不一定要建学校;
2/学校建的时候会选择在哪个城市来建校,所以关系应该由学校控制;所以城市的set集合inverse应该设置为true;
3/城市更名时,和学校的属性没有多大关系,房间和学生也不会更改任何属性.
4/一旦城市被炸毁,则学校也一同死掉,但学生很可能因为事先逃亡了,所以没有死,只是不上学了.因此
城市中的set节点应该是
<set name="schoolSet" cascade="delete" inverse="true">
<key column="city_id"></key>
<one-to-many class="com.test.TestSchool"></one-to-many>
</set>
学校中,多对一的城市节点,应该是 cascade=none, 即学校改名/被拆,和城市没有关系,不用级联操作;
<many-to-one name="city" column="city_id" cascade="none" class="com.test.TestCity" lazy="false"></many-to-one>
学校中的学生,同理,建校时,没有学生,学校被拆和学生没有关系;学生上学和学校有关系,学生改名或者死掉不会影响学校
<set name="students" inverse="true" cascade="none">
<key column="school_id"></key>
<one-to-many class="com.test.TestStudent"></one-to-many>
</set>
学生中的学校
<many-to-one name="school" column="school_id" class="com.test.TestSchool" cascade="none" lazy="false"></many-to-one>
**所以inverse 90%以上是由many来控制的,也是默认的.在one的一方,set标签中设置inverse=true.
cascade 90%不用设置是(默认值none).即一方改名和删除和另一方没有关系.如果像城市/学校这样,城市被毁,则学校被毁,就设置delete.
同样要设置delete的有 订单-订单明细; 用户-消费记录/登录记录;
以上的代码要改成
TestCity city = new TestCity();
city.setName("北京");
//创建城市,和玩游戏一样
HibernateSessionFactory.add(city);
//创建学校
TestSchool school = new TestSchool();
school.setName("北京一中");
//设置关系
school.setCity(city);
HibernateSessionFactory.add(school);
//学生出生
TestStudent student1 = new TestStudent();
student1.setName("张三");
HibernateSessionFactory.add(student1);
TestStudent student2 = new TestStudent();
student2.setName("李四");
HibernateSessionFactory.add(student2);
//学生1去上学
student1.setSchool(school);
HibernateSessionFactory.update(student1);
//学生2去上学
student2.setSchool(school);
HibernateSessionFactory.update(student2);
Hibernate: insert into TestCity (name) values (?)
Hibernate: insert into TestSchool (name, city_id) values (?, ?)
Hibernate: insert into TestStudent (name, school_id) values (?, ?)
Hibernate: insert into TestStudent (name, school_id) values (?, ?)
Hibernate: update TestStudent set name=?, school_id=? where id=?
Hibernate: update TestStudent set name=?, school_id=? where id=?
考虑对象关系时,考虑2点:
1/我的属性值改变或者我的存在消失,是否影响你的状态和你的消失,如果有影响则有cascade;
2/我们的关系,是你管理好,还是我管理好.如果你关系好,则设置inverse=true;many一方没有这个属性;
*/
}
/*
sql脚本
delete testSchool;
DELETE testCity;
DELETE testStudent;
DECLARE @school_id int;
INSERT INTO testCity (name) VALUES ('北京');
INSERT INTO testSchool (name,city_id) VALUES ('北京一中', @@identity);
SET @school_id= @@identity;
INSERT INTO testStudent (name,school_id) VALUES ('张三', @school_id);
INSERT INTO testStudent (name,school_id) VALUES ('李四', @school_id);
SELECT * from testCity
SELECT * from testSchool
SELECT * from testStudent
*/
}