Hibernate:脏字段跟踪

时间:2017-03-03 14:46:40

标签: java spring hibernate jpa byte-code-enhancement

我现在有点困惑。我会尝试尽可能简单地解释我的担忧:

我有一个Spring Boot应用程序,当然,它有实体。这些实体通过百万美元形式更新。在这种形式中,只有一些相关领域是可变的。例如,实体的name。此表单不会控制/更改其他字段,例如createdcreatedbylastUsedBy

现在是问题所在:如果我们现在更改实体,则所有其他字段都设置为null,因为它们不在请求中。一种方法是为这些缺少的字段<input type="hidden"/>输入添加。但这不是很优雅且容易出错。 我得出的结论是,hibernate应该只更新那些已被更改的字段。所以这必须通过DirtyTracking来完成。我目前有另一个应用程序,它使用OpenJPA和openJPA Enhancer,在这个应用程序中,更新只更新已更改的字段。我的假设是Hibernate增强器可以解决我的问题。但即使启用了脏跟踪,所有字段也会更新,信息也会丢失。 当我将@DynamicUpdate注释添加到给定实体时,我设法让它工作,但这不是正确的方法吗?

我仔细检查了实体是否得到了增强,并且还调试了spring / hibernate的整个保存过程。我在这里错过了什么吗?为什么hibernate还会更新所有非脏字段?

修改

我已经进一步检查并得到了这一点:代码来自AbstractEntityTuplizer

public void setPropertyValues(Object entity, Object[] values) throws HibernateException {
    boolean setAll = !entityMetamodel.hasLazyProperties();

    for ( int j = 0; j < entityMetamodel.getPropertySpan(); j++ ) {
        if ( setAll || values[j] != LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
            setters[j].set( entity, values[j], getFactory() );
        }
    }
}

值对象数组只有几个预先填充的值,只有更改/脏的值。例如values[5]values[6]。但是如果它们没有在函数的值paraemter中,则调用所有setter并将值设置为null。看起来像是一个bug。

3 个答案:

答案 0 :(得分:0)

使用@DynamicUpdate是一种方法,当实体使用@DynamicUpdate注释时,在更新阶段,hibernate不会为您未更改的列发出更新语句。这样,当您保存实体时,不会更改created,createdby和lastUsedBy。

请参阅: @DynamicUpdate @DynamicInsert

答案 1 :(得分:0)

最简单的方法是将修改合并到以下实体:

  1. 在两个HTTP请求之间保存在HttpSession或Redis(例如Spring Session)中。在这种情况下,请务必merge the detached entity
  2. 根据其标识符
  3. 获取第二个HTTP请求

    因此,一旦拥有该实体,您只需设置通过HTTP请求发送的属性,并let the dirty checking mechanism pick the changes

    这样,您不需要@DynamicUpdate,这适用于任何JPA提供商。

答案 2 :(得分:0)

我找到了另一种更适合我需求的解决方案:除了Spring MVC之外,问题不是Hibernate。

之前的一些代码:

@PutMapping("/{oid}/edit")
public String update(@ModelAttribute("oid") @Valid T entity,
                     BindingResult result,
                     Model model,
                     HttpServletRequest request)

至关重要的是@ModelAttribute("oid")。在我没有“oid”之前。所以Spring正在使用从表单中发布的模型,这显然不完整。仅使用表单输入。 通过在注释中添加“oid”,Spring MVC和Spring Data在应用表单字段之前获取实体。因此,我们有一个完整的实体,我们可以保存。

此解决方案接近@Vlad的第二个解决方案,但省略了更改字段的繁琐手动映射。

也许我必须为其他人提供更多信息才能全面了解我的问题!不然,谢谢!