论坛首页 Java企业应用论坛

Hibernate中的cascade和inverse

浏览 15947 次
精华帖 (1) :: 良好帖 (0) :: 新手帖 (1) :: 隐藏帖 (7)
作者 正文
   发表时间:2009-01-11   最后修改:2009-01-11

这两个属性都用于一多对或者多对多的关系中。而inverse特别是用于双向关系,在单向关系中我们并不需要。

 

Cascade代表是否执行级联操作,Inverse代表是否由己方维护关系。

 

Cascade:

 

Cascade属性的可能值有

    all: 所有情况下均进行关联操作,即save-update和delete。
    none: 所有情况下均不进行关联操作。这是默认值。
    save-update: 在执行save/update/saveOrUpdate时进行关联操作。
    delete: 在执行delete 时进行关联操作。

    all-delete-orphan: 当一个节点在对象图中成为孤儿节点时,删除该节点。比如在一个一对多的关系中,Student包含多个book,当在对象关系中删除一个book时,此book即成为孤儿节点。

 

Inverse:

 

Inverse属性的可能值是true或者false,默认为false:

 

     false代表由己方来维护关系,true代表由对方来维护关系。在一个关系中,只能由一方来维护关系,否则会出问题(解疑中会讲到);同时也必须由一方来维护关系,否则会出现双方互相推卸责任,谁也不管。

 

一多对的例子:

 

有两个类,Father和Child,是一对多的关系。下面这段hbm配置是从Father.hbm.xml中摘取的。

 

 

<set name="children" lazy="true" cascade="all" inverse="true">
    <key column="fatherid"/>
    <one-to-many class="my.home.Child"/>
</set>  

 

我们知道cascade和inverse的值对会有四种组合的可能(在此仅先假定cascade值为none或all)。

 

有如下一段代码:

 

FatherDao fatherDao = new FatherDao();

Father father = new Father("David");
Child child1 = new Child("David Junior One");
Child child2 = new Child("David Junior Two");

father.add(child1);
father.add(child2);

fatherDao.save(father);

 

1. 如果cascade="all"且inverse="false"时:

 

此时可以看到log里面:

 

// 执行对father的插入
Hibernate: insert into father (name) values (?)

// cascade = 'all',所以进行级联操作
Hibernate: insert into child (name, fatherid) values (?, ?)
Hibernate: insert into child (name, fatherid) values (?, ?)

// inverse = 'false',由father来维护关系(可以看到这些操作是多余的)
Hibernate: update child set fatherid =? where ID=?
Hibernate: update child set fatherid =? where ID=?
 

2. 如果cascade = "none" 且 inverse = "false":

 

// 执行对father的插入
Hibernate: insert into father (name) values (?)

// inverse='false',所以更新关系
Hibernate: update child set fatherid =? where ID=?

// 但由于cascade='none',child并未插入数据库,导致如下exception
org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance
 

3. 如果cascade = "all" 且 inverse = "true"

 

// 执行对father的插入
Hibernate: insert into father (name) values (?)

// cascade='all',执行对child的插入
Hibernate: insert into child (name, fatherid) values (?, ?)
Hibernate: insert into child (name, fatherid) values (?, ?)

// 但由于inverse='true',所以未有对关系的维护。但由于一对多的关系中,关系本身在“多”方的表中。所以,无需更新
关系。
 

4. 如果cascade = "none" 且 inverse = "true"

 

// 只执行对father的插入
Hibernate: insert into father (name) values (?)
 

可以看到,对于一对多关系,关系应由“多”方来维护(指定“一”方的inverse为true),并且应在“一”方指定相应的级联操作。

 

多对多:

 

在多对多关系中,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);
}
 
   发表时间:2009-01-13  
不错,看明白了!
0 请登录后投票
   发表时间:2009-01-13  
但实际应用中,会不会更多的是代码维护关联关系呢?
0 请登录后投票
   发表时间:2009-02-06  
all-delete-orphan: 当一个节点在对象图中成为孤儿节点时,删除该节点。比如在一个一对多的关系中,Student包含多个book,当在对象关系中删除一个book时,此book即成为孤儿节点。

这里还不太明白。。。
0 请登录后投票
   发表时间:2009-02-09  
“孤儿”是建立在父子关系基础上的,这里的父子关系不是指继承上的父子关系,而是指一个类的持久化生命周期必须由另外一个类来控制,即父方控制子方的持久化生命周期。我来举一个例子解释什么叫“孤儿”,

我现在有个类叫Item,还有个类叫Bid,Item指一个商品的条目,Bid为包含该条目的订单,在这里,我们规定order对象必须和一个item对象关联,脱离了item的order对象所代表的数据记录将被从数据库删除。设一个Item可以包含多个Bid,则在Item的POJO中,有如下这样一个集合:
private Set<Bid> bids = new HashSet<Bid>();

当然还有相应的获取器方法(即get/set)。
而在Bid类中,有个对Item的引用:
private Item item;

在Item的映射文件中,有如下一段
<set name="bids" cascade="all-delete-orphan" inverse="true">
           <key column="item_id"/>
           <one-to-many class="persistence.domain.parentAndChild.Bid"/>
</set>



当我们移除Item对象和一个Bid对象的关联关系后,即进行类似如下操作后
item.getBids.remove(order);
order.setItem(null);

这个特定的order就是一个孤儿了,明白了?
当我们执行上面操作时,Hibernate会执行类似下面的SQL语句:delete from bids where item_id=? and bid_id=?

另外,注意下面两点
当保存或者更新Item对象时候,级联保存相关联的Bid对象,相当于cascade属性为“save-update”
当删除Item对象时候,级联删除所有关联的Bid对象,相当于cascade属性为“delete”
呵呵,不知道说明白没有。
0 请登录后投票
   发表时间:2009-02-10  
谢谢iranger的解释。。。

不过这样all-delete-orphan好像就跟all没什么区别了。。。
0 请登录后投票
   发表时间:2009-02-11  
不过这样all-delete-orphan好像就跟all没什么区别了。。。
________________________________________________________

当然有区别。
casacde属性设置为“all”,则包括了save-update和delete的行为
all-delete-orphan包括了all和delete-orphan的行为

你可以自己修改后看看Hibernate生成的sql语句就知道了
0 请登录后投票
   发表时间:2009-03-03  
wangneng_001 写道
all-delete-orphan: 当一个节点在对象图中成为孤儿节点时,删除该节点。比如在一个一对多的关系中,Student包含多个book,当在对象关系中删除一个book时,此book即成为孤儿节点。

这里还不太明白。。。


脱离与student的关系,
student.books.remove(book)
此时,它成了孤儿。即使程序没有book.setStudent(null),book也将被hibernate认为是孤儿,hibernate将删除之,并自动book.setStudent(null)。

单纯的只是book.student = null, hibernate不会认为book是孤儿。
在事务提交时,hibernate将更新数据库中book.student_id为null,如果数据库约束该字段不能为null,则抛出异常。
且即便是事务可以成功提交,student.books.contains(book)仍然成立!程序开始出现Bug 

BTW,当cascade=all-delete-orphan,无论inverse是true或false,以上结果仍成立,只是false的情况会多产生一条update。
这也再次验证cascade和inverse其实没有什么关系,经常把两个放一起讨论时常产生误导……
0 请登录后投票
   发表时间:2009-03-03  
wangneng_001 写道
谢谢iranger的解释。。。

不过这样all-delete-orphan好像就跟all没什么区别了。。。


hi.

session有套api用来update和delete,在改变了对象之后调用可以看到相应的更新或者删除语句。
但是在hibernate中,你不需要显示调用session.update或者delete,在改变了对象之后,直接拿到事务commit就能发现数据库状态也被改变了。
事实上,session.update和delete只是生成一条更新或者删除语句,最终都是清理缓存时才执行
实质上,删除或者不删除与生成的SQL语句有关,处于一个session的持久对象,在经历了一些逻辑之后,有些对象可能要被删除,它们被解除了关联关系,部分对象成了孤儿,但你并没有调用session.delete方法,所以删除这些对象的SQL就没有生成,而你的cascade=all,hibernate对比缓存状态,只是把外键更新成了null,当cascade=all-delete-orphan时情况完全不同,hibernate为那些孤儿对象生成删除语句,并很果断地执行了这些语句。始终注意一个前提:你没有显示地调用session.delete方法。
当你显示地调用了session.delete方法时,cascade=all或者all-delete-orphan就确实没有区别。

任何东西显示得写出来都容易明白,JPA里就没有类似all-delete-orphan的CascadeType
0 请登录后投票
   发表时间:2009-03-07  
引用
Father和Child,是一对多的关系。

应该是child1.add(father);吧?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics