`
- 浏览:
71726 次
- 性别:
- 来自:
上海
-
deleted object would be re-saved by cascade
Let's Play "Who Owns That Collection?" With Hibernate
If you have used Hibernate and mapped a one-to-many relationship you've probably come across the "delete orphan" feature. This feature offers cascade delete of objects orphaned by code like the following:
Preference pref = getSomePreference();
user.getPreferences().remove(pref);
In the above code, a specific Preference is removed from a User. With the delete orphan feature, and assuming there is an active transaction associated with a session, the preference that was removed from the user is automatically deleted from the database when the transaction commits. This feature is pretty handy, but can be tricky if you try to write clever code in you setter methods, e.g. something like this:
// Do not do this!
public void setPreferences(Set newPreferences) {
this.preferences = newPreferences == null ? new HashSet<Preference>() : newPreferences;
}
Code like the above results in a HibernateException with the following message if you pass null into setPreferences and try to save the user object:
A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance
What is happening here is that Hibernate requires complete ownership of the preferences collection in the User object. If you simply set it to a new object as in the above code sample, Hibernate is unable to track changes to that collection and thus has no idea how to apply the cascading persistence to your objects! The same error will occur if you passed in a different collection, e.g.:
user.setPreferences(new HashSet<Preference>());
So the point is that Hibernate's delete orphan abstraction is leaking into your domain model object. This is pretty much unavoidable but is a leaky abstraction nonetheless that developers need to be aware of lest they run into the error mentioned above.
So how can you avoid this problem? The only sure way that I know of is to make the setter method private, since passing any new collection or null results in the "owning entity" error. This way only Hibernate will use the setter method to load up user objects (it invokes the method reflectively after setting it accessible via the Reflection API). Then you could add a method addPreference to your code which is the public API for adding preferences. Anyone could of course use reflection to do the same thing Hibernate is doing, but then all bets are off as they are subverting your public API. For example:
public void addPreference(Preference p) {
getPreferences().add(p);
p.setUser(this);
}
This has the nice side effect of establishing the bi-directional relationship between user and preference, assuming your model allows bi-directional navigation. You could also add a null check if you are paranoid. Removing a preference from a user is equally simple. You can write a helper method removePreference or you could call the getter and then call remove as shown here:
user.getPreferences().remove(aPref);
Essentially, you can operate on the collection returned by getPreferences as a normal Java collection and Hibernate will do the right thing. Since you are operating on the collection maintained and observed by Hibernate, it is able to track your changes, whereas replacing the collection wholesale makes Hibernate really mad, since it believes it is the sole proprietor of the collection, not you! For example, if you want to remove all the user's preferences you could write the following:
user.getPreferences().clear();
Note that all the above discussion refers to one-to-many relationships that specify the delete orphan cascade type; usually you will specify both "all" and "delete-orphan." In cases where you are only using the "all" cascade option, the semantics are quite different. Assuming the normal case where the "many" side of the relationship owns the relationship -- i.e. you used inverse="true" in your mapping file or @OneToMany(mappedBy = "user") if using annotations - then you must explicitly delete the child objects as Hibernate will only track that side of the relationship. For example, if cascade is "all" and you remove a preference from a user and then save the user, nothing happens! You would need to explicitly delete the preference object, as shown here:
// Assume only using cascade="all" and an inverse="true" mapping in User.hbm.xml
user.getPreferences().remove(aPref); // does not cause database delete
session.delete(aPref); // this causes the database deletion
One last thing to note in the above is that you must remove aPref from the user, or else Hibernate will throw an exception stating that the preference would be re-saved upon cascade! So if the User object is in the Session, remember you need to undo both sides of the relationship for things to work properly.
分享到:
Global site tag (gtag.js) - Google Analytics
相关推荐
在使用Hibernate进行数据库操作时,有时会遇到一种异常情况,即在尝试删除一个对象时,系统抛出"deleted object would be re-saved by cascade (remove deleted object from associations)"的异常。这个异常通常发生...
本文将深入探讨如何在Laravel中实现级联删除,并结合"laravel-cascade-deletes"这个示例来说明。 1. **Eloquent ORM与级联删除**: Laravel中的Eloquent ORM(对象关系映射)是PHP世界中最优雅的ORM之一,它提供了...
Exploring the location of object deleted by seam-carving
《微信好友恢复:深入解析“wechat-deleted-friends-master”项目》 在我们的日常生活中,微信作为一款全球广泛使用的社交应用,已经成为了我们联系亲朋好友、进行商务沟通的重要工具。然而,有时由于误操作或者...
"2BE_DELETED_vdr-plugin-arghdirector:暂时的? 被遗弃的家? VDR插件Arghdirector-开源" 这个标题提到了一个名为"vdr-plugin-arghdirector"的VDR(Video Disk Recorder)插件。VDR是一种开源的数字视频录像机软件...
Multiply deleted [E1, polymerase-, and pTP-] adenovirus vector persists despite deletion of the preterminal protein RESEARCH ARTICLE Multiply deleted [E1, polymerase-, and pTP-] adenovirus vector ...
Alfresco删除的内容商店清洁程序 该加载项提供了一项工作,以删除“已删除的内容存储”中... 该插件将从已删除的内容存储库(通常位于${alfresco}/alf_data/contentstore.deleted ) ${alfresco}/alf_data/contentstore
语言:English 追踪谁在社交网络上使用谁。 看谁删除/联合您在社交网络上! 谁删除了我跟踪你的朋友列表,并在朋友缺少时通知你。 我们会告诉您是否删除了您或停用。 当你结交新朋友时,我们也会告诉你!
{****************************************************************************** * * ... Gets an indexed child object of the designated object. Parameters: In: inRef - The reference...
SQLite-Deleted-Records-Parser, 在SQLite数据库中,用于恢复已经删除项目的脚本 SQLite解析器在SQLite数据库中恢复删除条目并将输出放入TSV文件或者文本文件( R ) 中的脚本sqlparse.py的###Usagesqlparse.py -f/...
Dont Download, it will be deleted
### SQL Inserted 和 Deleted 表详解 在 SQL Server 中,`Inserted` 和 `Deleted` 是两个特殊的表,它们主要用于触发器(Trigger)中。当执行 INSERT、UPDATE 或 DELETE 操作时,系统会自动创建这两个虚拟表,并...
They can be saved to another drive with a few mouseclicks (see Searching deleted files for step by step instructions). Recover4all?only lists folders that contain deleted files. Normal nondeleted ...
- ADD: Add TFlexPanel.InvalidateControl virtual method which calls from TFlexControl.Invalidate and can be overriden (it is possible now to catch all object invalidation calls). - FIX: The TFlexPanel....
that would be deleted. No note is added if a file is not supposed to be removed by Symantec Uninstaller. -f Do not show files as they are removed. If -f is not specified, file names are shown as ...
在IT行业中,文件管理是日常操作的重要组成部分,无论是个人用户还是企业用户,都需要掌握如何有效地创建、保存、移动以及删除文件。在这个场景中,我们关注的是如何删除一个名为"StudentManagement"的压缩包文件。...
37. **Constant object cannot be passed as var parameter** - **含义**: 无法将常量对象作为变参传递。 - **解决办法**: 使用可变对象或调整参数类型。 38. **Constant or type identifier expected** - **...
xvideos.com-deleted-full.csv.zip