繁体   English   中英

除了遍历每个字段之外,是否有更简单的方法来告知JPA实体已被修改?

[英]Is there an easier way to tell that a JPA Entity has been modified other than looping through each field?

我有一个自定义的EmptyInterceptor ,可用于通过覆盖onSaveonFlushDirty来设置有关创建日期,上次修改日期,用户创建和上次修改用户的onFlushDirty

这个系统对我们来说运作良好,但是我们发现一个问题,就是我们的代码使用一些输入数据盲目地调用实体上的设置器。 在这种情况下,即使数据没有更改,休眠也会触发我们的自定义拦截器,并设置最后修改的用户和日期。 这将导致Hibernate更新数据库中的实体。 我们知道这一点是因为我们在此表上有一个插入和更新触发器。 如果禁用拦截器,则Hibernate将不会更新数据库中的实体。

如果实体没有真正更改,我们希望避免设置用户和日期,因此在这种情况下不会发生任何更新。 我已经了解了Hibernate进行脏实体检查的方式,并且已经将previousStatecurrentState数组传递到onFlushDirty ,我可以循环执行此检查。 有没有更简单的方法可以做到这一点?

我查看了HibernateSession.isDirty(),但这并没有告诉我这个特定的实体是否已更改,即使会话中有更改的实体也是如此。


更新

事实证明,有问题的代码盲目地调用setter并不是问题。 设置子对象的集合而不是修改已经存在的集合是令人讨厌的代码。 在这种情况下,Hibernate认为该实体已发生更改-或至少认为足以调用Interceptor。

从设计的角度来看,该检查实际上应该在前端/客户端进行。 如果前端确定该记录已由用户修改,则它应将更新提交给服务器。

如果要在中间层(服务器端)执行此操作,则应考虑Hibernate实体的生命周期:瞬态,持久性,分离,已删除,还应考虑session.save()session.merge()有何不同session.merge()session.saveOrUpdate()

此外,在管理会话时,还必须考虑不同的设计模式,例如“每次操作会话” 反模式 ,每次请求会话或每次会话会话模式等。

如果您有一个打开的会话,并且您的实体已经在该会话中(通过另一个操作),则Hibernate实际上可以为您进行脏检查。 但是,如果您有一个分离的实体,并且该实体在会话中不存在,并说您进行了merge ,则Hibernate首先会通过发出SELECT从数据存储(数据库)中获取该实体,并将该托管实体放入持久性存储中。上下文,然后它将合并两个实体,一个是您提供的实体,另一个是休眠的实体,并将其放入持久性上下文中,然后检查是否有任何更改,从而进行脏检查。

在您的情况下,由于要从脏检查中排除最后修改的用户名和时间,因此最好通过ID获取实体(因为您可能在分离的实体上拥有ID),然后使用equals()hashCode()来执行您自己的脏检查版本,如果不是,则调用merge。

您可能会认为这是对数据库的一次额外旅行,但事实并非如此,因为即使在正常情况下,如果该实体尚未处于持久性上下文(即会话)中并且如果该实体还处于休眠状态,Hibernate仍会进行对数据库的额外旅行。是的,并且您执行一个获取ID,Hibernate只会返回给您会话中已经拥有的内容,而不会访问数据库。

此参数不适用于saveOrUpdate,在这种情况下,Hibernate会直接将更新推送到数据库,而不会进行脏检查(如果实体不在会话中),并且如果它已在会话中,它将抛出一个说该实体已经在会话中的异常。

万一任何人需要解决相同的问题而无需更改导致此问题的代码,我将比较实现为:

    /**
     * Called upon entity UPDATE. For our BaseEntity, populates updated by with logged in user ID and
     * updated date-time.
     * <p>
     * Note that this method is called even if an object has NOT changed but someone's called a setter on
     * a Collection related to a child object (as opposed to modifying the Collection itself). Because of
     * that, the comparisons below are necessary to make sure the entity has really changed before setting
     * the update date and user.
     *
     * @see org.hibernate.EmptyInterceptor#onFlushDirty(java.lang.Object, java.io.Serializable, java.lang.Object[], java.lang.Object[], java.lang.String[], org.hibernate.type.Type[])
     */
    @Override
    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState,
            String[] propertyNames, Type[] types)
    {

        boolean changed = false;

        if (entity instanceof BaseEntity) {
            logger.debug("onFlushDirty method called on " + entity.getClass().getCanonicalName());

            // Check to see if this entity really changed(see Javadoc above).
            boolean reallyChanged = false;
            for (int i = 0; i < propertyNames.length; i++) {
                // Don't care about the collection types because those can change and we still don't consider
                // this object changed when that happens.
                if (!(types[i] instanceof CollectionType)) {
                    boolean equals = Objects.equals(previousState[i], currentState[i]);
                    if (!equals) {
                        reallyChanged = true;
                        break;
                    }
                }
            }

            if (reallyChanged) {
                String userId = somehowGetUserIdForTheUserThatMadeTheRequest();
                Date dateTimeStamp = new Date();

                // Locate the correct field and update it.
                for (int i = 0; i < propertyNames.length; i++) {
                    if (UPDATE_BY_FIELD_NAME.equals(propertyNames[i])) {
                        currentState[i] = userId;
                        changed = true;
                    }

                    if (UPDATE_DATE_FIELD_NAME.equals(propertyNames[i])) {
                        currentState[i] = dateTimeStamp;
                        changed = true;
                    }
                }
            }
        }

        return changed;
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM