【问题标题】:Hibernate - proper way to update collection which is mapped with cascading type all-delete-orphanHibernate - 更新与级联类型 all-delete-orphan 映射的集合的正确方法
【发布时间】:2013-12-13 06:34:03
【问题描述】:

如果集合的“父级”与 all-delete-orphan 映射,正确和简单的更新集合方法是什么?对象?
通过更新,我不仅仅指从集合中添加/删除项目,还包括更新项目属性的值(当然对于之前在集合中的那些项目)。

场景是有一个 Parent 对象,它有一个 Child 对象的集合,并且用户可以通过一种形式编辑 Children 集合 - 添加/删除孩子,但也可以编辑孩子的属性 (在同一表格上)。

基本上我想要这样的东西:

Parent parent = session.get(Parent.class, parentUI.getId());
parent.setChildren(parentUI.getChildren());  // parentUI is a DTO
session.saveOrUpdate(parent);

这不起作用,我理解原因,但在我看来,这应该是使用休眠和开发 UI 应用程序时非常常见的情况,所以我正在寻找一个(按书本)解决方案。

我使用的是 hibernate 3.6.10 和基于 XML 的配置。
这是相关的映射(我使用 ArrayList 来存储子集合):

<list name="children" cascade="all, delete-orphan">
    <key column="parent_id" not-null="true"/>
    <list-index column="ordinal" />
    <one-to-many class="Child" />
</list>

如果重要的话,子对象也有以相同方式映射的集合,但我认为这不相关,因为这是一个与所描述的问题相同的问题。

顺便说一句。我为此浪费了一整天的时间,当然还检查了数十个与此非常相似的问题,但还没有找到合理的解决方案或解决此问题的模式。也许我错过了什么?

【问题讨论】:

  • 尝试清除子列表,然后调用'setChildren'方法
  • parent.setChildren(parentUI.getChildren());如果要保存分离的对象,可以尝试 merge() 方法。这可能会删除现有值,请谨慎使用。
  • 在当前会话中从数据库中提取数据并尝试更新同一会话上下文中的值时使用 saveOrUpdate。
  • @Zeus 不幸的是,但是我尝试(合并或 saveOrUpdate)我得到“org.hibernate.HibernateException:拥有的实体实例不再引用具有 cascade="all-delete-orphan" 的集合: “。对此我确实理解它发生的原因,
  • @Dav Blanked that won't work - NonUniqueObjectException 被抛出,因为在更新现有子项的情况下,可能有两个具有相同标识的实例 - 一个从 DB 加载,一个从 UI 加载(它不'是否先清理集合无关紧要 - 子对象仍绑定到 Hibernate 会话)

标签: hibernate spring-mvc orm mapping all-delete-orphan


【解决方案1】:

好吧,因为我没有找到书本的解决方案,所以我首先手动通过在我的父对象parent.updateChildren(parentUI.getChildren())上创建一个自定义方法来解决这个问题,它会迭代通过旧集合,将其与 UI 中的更新集合进行比较并手动更新。不用说,这远非理想。

然后我的一位同事 (Zoran Regvart) 给了我一个机智的想法(考虑到我正在使用的技术,例如 Spring MVC)来稍微改进一下。
想法是,如果提交更新父表单的表单,在 spring (web) binder 完成工作之前,我们可以从 DB 加载集合并将其设置为命令对象 (parentUI),从而将其“种植”到由 spring binder 更新(换句话说,将集合更新过程委托给他 - 他无论如何都会做的事情)。

这是(伪)代码:

@ModelAttribute("parentUI")
public ParentUI initParentUI(HttpServletRequest p_request) {
    ParentUI parentUI = new ParentUI();
    String parentId = p_request.getParameter("id");
    if (parentId != null) { // indicates a form submit
        Parent parent = session.get(Parent.class, parentUI.getId());
        parentUI.setChildren(parent.getChildren());
    }
    return parentUI;
}

这样当 spring binder 完成他的工作时,他将更新已经绑定到 Hibernate session 的集合。

这个解决方案也不是我很满意的东西,我还没有在很多情况下进行测试,但对于我当前的需求(对我来说),它肯定是一种改进。

更新:此解决方法仅在添加新子级或编辑其属性的情况下很方便。对于删除子级或更改其顺序的情况,最好避免,因为这会增加复杂性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-22
    • 2011-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多