有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java除了在每个字段中循环外,还有更简单的方法来判断JPA实体是否已被修改?

我有一个自定义的EmptyInterceptor,我使用它来设置创建日期、上次修改日期、由用户创建的日期,以及通过覆盖onSaveonFlushDirty最后修改的用户的信息

这个系统对我们来说运行得很好,但我们刚刚发现了一个问题,我们的代码使用一些输入数据盲目地调用实体上的setter。在这种情况下,即使数据没有更改,hibernate也会启动我们的自定义拦截器,并设置上次修改的用户和日期。这会导致Hibernate更新数据库中的实体。我们之所以知道这一点,是因为我们在这个表上有一个insert和update触发器。如果禁用拦截器,Hibernate将不会更新数据库中的实体

如果实体没有真正更改,我们希望避免设置用户和日期,因此在这些情况下不会发生更新。我读过Hibernate执行dirty entity checking的方式,我将previousStatecurrentState数组传递到onFlushDirty中,我可以自己循环执行这个检查。有更简单的方法吗

我查看了HibernateSession.isDirty(),但这并不能告诉我这个特定的实体是否发生了变化,只是会话中有一个发生了变化的实体


更新

事实证明,错误的代码盲目地调用setter并不是问题所在。错误的代码是设置子对象集合,而不是修改已经存在的集合。在这种情况下,Hibernate认为实体已经改变了,或者至少认为已经足够调用拦截器了


共 (1) 个答案

  1. # 1 楼答案

    从设计角度来看,这种检查实际上应该在前端/客户端完成。如果前端确定用户修改了记录,则应向服务器提交更新

    如果你想在中间层(服务器端)这样做,那么你应该考虑Hibernate实体的生命周期:短暂的、持久的、分离的、移除的,并且考虑^ {< CD1>}与^ {CD2>}和^ {CD3>}不同。p>

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

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

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

    您可能会认为这是对DB的额外访问,但事实并非如此,因为即使在正常情况下,Hibernate仍然会对DB进行额外访问,如果实体还没有在持久性上下文(即会话)中,如果实体已经在持久性上下文中,并且您执行了get-by id,Hibernate只会返回到会话中已经存在的内容,而不会命中DB

    此参数不适用于saveOrUpdate,在这种情况下,Hibernate只需将更新推送到DB,而不进行脏检查(如果实体尚未在会话中),如果实体已在会话中,则会抛出异常,表明实体已在会话中