精华帖 (1) :: 良好帖 (0) :: 新手帖 (1) :: 隐藏帖 (7)
|
|
---|---|
作者 | 正文 |
发表时间:2009-01-11
最后修改:2009-01-11
这两个属性都用于一多对或者多对多的关系中。而inverse特别是用于双向关系,在单向关系中我们并不需要。
Cascade代表是否执行级联操作,Inverse代表是否由己方维护关系。
Cascade:
Cascade属性的可能值有 all:
所有情况下均进行关联操作,即save-update和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); } 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-01-13
不错,看明白了!
|
|
返回顶楼 | |
发表时间:2009-01-13
但实际应用中,会不会更多的是代码维护关联关系呢?
|
|
返回顶楼 | |
发表时间:2009-02-06
all-delete-orphan: 当一个节点在对象图中成为孤儿节点时,删除该节点。比如在一个一对多的关系中,Student包含多个book,当在对象关系中删除一个book时,此book即成为孤儿节点。
这里还不太明白。。。 |
|
返回顶楼 | |
发表时间: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” 呵呵,不知道说明白没有。 |
|
返回顶楼 | |
发表时间:2009-02-10
谢谢iranger的解释。。。
不过这样all-delete-orphan好像就跟all没什么区别了。。。 |
|
返回顶楼 | |
发表时间:2009-02-11
不过这样all-delete-orphan好像就跟all没什么区别了。。。
________________________________________________________ 当然有区别。 casacde属性设置为“all”,则包括了save-update和delete的行为 all-delete-orphan包括了all和delete-orphan的行为 你可以自己修改后看看Hibernate生成的sql语句就知道了 |
|
返回顶楼 | |
发表时间: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其实没有什么关系,经常把两个放一起讨论时常产生误导…… |
|
返回顶楼 | |
发表时间: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 |
|
返回顶楼 | |
发表时间:2009-03-07
引用 Father和Child,是一对多的关系。
应该是child1.add(father);吧? |
|
返回顶楼 | |