Cascade与inverse这两个属性都用于一对多或者多对多的关系中。而inverse特别是用于双向关联关系,在单向关联关系中我们并不需要。
cascade代表是否执行级联操作,inverse代表是否由己方维护关系。
cascade:
cascade的属性有:
1·all:所有操作情况下都进行级联操作,即save-update和delete
2·none:所有情况下均不进行级联操作。这是默认值。
3·save-update:执行save/update/saveOrUpdate时进行级联操作。
4·delete:在执行delete操作的时候进行级联操作。
5·all-delete-orphan:当一个对象节点在对象图中成为孤儿节点时,删除该节点。比如在一个一对多的关系中,student包含多个book,当在对象关系中删除一个book时,此book即成为孤儿节点。
Inverse:
inverse属性值true或者false,默认值false(即默认己方维护关联关系)
false:代表有己方来维护关系,true代表由对方来维护关联关系。
在一个关系中,只能有一方来维护关系,否则会出问题;同时也必须有一方维护关系,否则会出现双方互相推卸责任,谁也不管。
domain对象:
映射文件:
如果cascade取值分为 all 和 none ; inverse取值分为 false 和 true,这两种情况,那么应该有四种组合。
cascade = all,inverse = false
cascade = all,inverse = true
cascade = none,inverse = false
cascade = none,inverse = true
下面分别测试:
1·cascade = all,inverse = false
测试代码:
执行的sql语句:
2· cascade = all,inverse = true
执行的sql语句:
也就是说Child的对象的外键为空了!!
当然此时如果测试代码改成如下方式:
执行的sql语句为
但是数据库中的数据都是完整的了!!!
3·cascade = none,inverse = false
执行的sql语句
级联取消,所以没有同时保存child,这时及时去跟新child的外键,只能出错了
4·cascade = none,inverse = true
执行的sql语句
cascade=none,没有级联保存child,所以child表中没有数据;inverse=true,放弃维护关联关系,所以没有去跟新child的外键,所以就不会报异常
多对多:
在多对多关系中,inverse可以为任何一方,没有什么区别。
解疑:
为什么在多对多中不能由双方都来维护关系了:因为这样会导致重复更新中间表的可能,报出重复值的错误。
那么如何在多对多的双向关联中使双方都能维护关系:最好让控制关系的那方来更新关系,如果想让另一方也来维护关系,那么只有在操作这一方的数据时“显式”更新中间表了吧。
注意:
同时注意在双向关联中,对象之间的关联跟上面提及的关系表维护没有关系。一个是对象/java层面的,一个是hibernate数据库层面的。如果你想在更新一方时,也更新另一方的对象集合,请看下面这段代码:
这是Person类中的一段代码,Person和Event是多对多的双向关联关系,他们在对方类中的集合分别为participants和events。关系表由Person维护,所以对象关系的维护也在Person类中,而不是Event类中
cascade代表是否执行级联操作,inverse代表是否由己方维护关系。
cascade:
cascade的属性有:
1·all:所有操作情况下都进行级联操作,即save-update和delete
2·none:所有情况下均不进行级联操作。这是默认值。
3·save-update:执行save/update/saveOrUpdate时进行级联操作。
4·delete:在执行delete操作的时候进行级联操作。
5·all-delete-orphan:当一个对象节点在对象图中成为孤儿节点时,删除该节点。比如在一个一对多的关系中,student包含多个book,当在对象关系中删除一个book时,此book即成为孤儿节点。
Inverse:
inverse属性值true或者false,默认值false(即默认己方维护关联关系)
false:代表有己方来维护关系,true代表由对方来维护关联关系。
在一个关系中,只能有一方来维护关系,否则会出问题;同时也必须有一方维护关系,否则会出现双方互相推卸责任,谁也不管。
domain对象:
import java.util.HashSet;
import java.util.Set;
public class Father {
private Integer id;
private String name;
private Set<Child> children = new HashSet<Child>();
public Father(){}
public Father(String name){
this.name = name;
}
public void addChild(Child child){
if (!this.children.contains(child)) {
this.children.add(child);
}
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Child> getChildren() {
return children;
}
public void setChildren(Set<Child> children) {
this.children = children;
}
}
public class Child {
private Integer id;
private String name;
private Father father;
public Child(){}
public Child(String name){
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Father getFather() {
return father;
}
public void setFather(Father father) {
this.father = father;
}
}
映射文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.hibernate.domain.inversecascade">
<class name="Father" table="father">
<id name="id" unsaved-value="-1">
<generator class="native" />
</id>
<property name="name"/>
<!-- cascade="all"且inverse="false" -->
<set name="children" cascade="all" inverse="false">
<key column="fatherid"/>
<one-to-many class="Child"/>
</set>
</class>
</hibernate-mapping>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.hibernate.domain.inversecascade">
<class name="Child" table="child">
<id name="id" unsaved-value="-1">
<generator class="native" />
</id>
<property name="name"/>
<many-to-one name="father" column="fatherid"/>
</class>
</hibernate-mapping>
如果cascade取值分为 all 和 none ; inverse取值分为 false 和 true,这两种情况,那么应该有四种组合。
cascade = all,inverse = false
cascade = all,inverse = true
cascade = none,inverse = false
cascade = none,inverse = true
下面分别测试:
1·cascade = all,inverse = false
测试代码:
public static void main(String[] args) {
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Father father = new Father("father");
Child child1 = new Child("child 1");
Child child2 = new Child("child 2");
father.addChild(child1);
father.addChild(child2);
session.save(father);
tx.commit();
session.close();
}
执行的sql语句:
Hibernate: insert into father (name) values (?)
Hibernate: insert into child (name, fatherid) values (?, ?)
Hibernate: insert into child (name, fatherid) values (?, ?)
Hibernate: update child set fatherid=? where id=?
Hibernate: update child set fatherid=? where id=?
2· cascade = all,inverse = true
<!-- cascade="all"且inverse="true" -->
<set name="children" cascade="all" inverse="true">
<key column="fatherid"/>
<one-to-many class="Child"/>
</set>
执行的sql语句:
Hibernate: insert into father (name) values (?)
Hibernate: insert into child (name, fatherid) values (?, ?)
Hibernate: insert into child (name, fatherid) values (?, ?)
也就是说Child的对象的外键为空了!!
当然此时如果测试代码改成如下方式:
public static void main(String[] args) {
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Father father = new Father("father");
Child child1 = new Child("child 1");
child1.setFather(father);
Child child2 = new Child("child 2");
child2.setFather(father);
father.addChild(child1);
father.addChild(child2);
session.save(father);
tx.commit();
session.close();
}
执行的sql语句为
Hibernate: insert into father (name) values (?)
Hibernate: insert into child (name, fatherid) values (?, ?)
Hibernate: insert into child (name, fatherid) values (?, ?)
但是数据库中的数据都是完整的了!!!
3·cascade = none,inverse = false
<!-- cascade="none"且inverse="false" -->
<set name="children" cascade="none" inverse="false">
<key column="fatherid"/>
<one-to-many class="Child"/>
</set>
执行的sql语句
Hibernate: insert into father (name) values (?)
Hibernate: update child set fatherid=? where id=?
Exception in thread "main" org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing:
级联取消,所以没有同时保存child,这时及时去跟新child的外键,只能出错了
4·cascade = none,inverse = true
<!-- cascade="none"且inverse="true" -->
<set name="children" cascade="none" inverse="true">
<key column="fatherid"/>
<one-to-many class="Child"/>
</set>
执行的sql语句
Hibernate: insert into father (name) values (?)
cascade=none,没有级联保存child,所以child表中没有数据;inverse=true,放弃维护关联关系,所以没有去跟新child的外键,所以就不会报异常
多对多:
在多对多关系中,inverse可以为任何一方,没有什么区别。
解疑:
为什么在多对多中不能由双方都来维护关系了:因为这样会导致重复更新中间表的可能,报出重复值的错误。
那么如何在多对多的双向关联中使双方都能维护关系:最好让控制关系的那方来更新关系,如果想让另一方也来维护关系,那么只有在操作这一方的数据时“显式”更新中间表了吧。
注意:
同时注意在双向关联中,对象之间的关联跟上面提及的关系表维护没有关系。一个是对象/java层面的,一个是hibernate数据库层面的。如果你想在更新一方时,也更新另一方的对象集合,请看下面这段代码:
这是Person类中的一段代码,Person和Event是多对多的双向关联关系,他们在对方类中的集合分别为participants和events。关系表由Person维护,所以对象关系的维护也在Person类中,而不是Event类中
public void addToEvent(Event event) {
this.getEvents().add(event);
event.getParticipants().add(this);
}
public void removeFromEvent(Event event) {
this.getEvents().remove(event);
event.getParticipants().remove(this);
}