Java 休眠环境在没有更改时创建记录

Java 休眠环境在没有更改时创建记录,java,hibernate-envers,Java,Hibernate Envers,我一直在寻找一种方法,使envers不记录我合并的任何实体,因为自上次记录以来没有任何修改。 我发现这应该是Envers的正常行为,如果没有修改,就没有审计 实体只有@Audited注释,但即使自上次审核以来没有任何更改,它们也会继续被审核。 这是my Persience.xml的配置: 我找到了这个,但没有答案 我的一些equals/hascode方法只测试主键的ID,但我没有发现任何与此相关的主题 我还看到了一个新的参数,可以看到哪个字段发生了变化,但我认为这与我的问题无关 我正在使用Pos

我一直在寻找一种方法,使envers不记录我合并的任何实体,因为自上次记录以来没有任何修改。 我发现这应该是Envers的正常行为,如果没有修改,就没有审计

实体只有@Audited注释,但即使自上次审核以来没有任何更改,它们也会继续被审核。 这是my Persience.xml的配置:

我找到了这个,但没有答案

我的一些equals/hascode方法只测试主键的ID,但我没有发现任何与此相关的主题

我还看到了一个新的参数,可以看到哪个字段发生了变化,但我认为这与我的问题无关

我正在使用Postgresql,如果这很重要的话


对这种行为有什么想法吗?目前我唯一的解决方案是通过entityManager获取实体,并对它们进行比较。如果出现这种情况,我将使用一些基于反射的API。

问题不是来自应用程序,而是来自代码本身。我们的entites有一个字段lastUpdateDate,它在每次合并时设置为当前日期。合并后,比较由envers完成,因此自上次修订以来,此字段已更改

对于那些好奇的人来说,版本之间的更改在org.hibernate.envers.internal.entities.mapper.MultiPropertyMapper.map中进行评估,至少在evers 4.3.5.Final上进行评估,如果oldState和newState之间有任何更改,则返回true。它根据所比较的属性使用特定的映射器

编辑:我将在这里说明我是如何解决这个问题的,但也可以使用达格玛的解决方案。然而,我的可能有点棘手和肮脏

我使用了Envers的EnversPostUpdateEventListenerImpl,如和中所述:我创建了我的Envers,并强制Envers使用它

@Override
public void onPostUpdate(PostUpdateEvent event) {
    //Maybe you should try catch that !
    if ( event.getOldState() != null ) {
        final EntityPersister entityPersister = event.getPersister();
        final String[] propertiesNames = entityPersister.getPropertyNames();

        for ( int i = 0; i < propertiesNames.length; ++i ) {
            String propertyName = propertiesNames[i];
            if(checkProperty(propertyName){
                event.getOldState()[i] = event.getState()[i];
        }
    }
    // Normal Envers processing
    super.onPostUpdate(event);
}

注意:您必须重新实现所有事件侦听器,即使它们只是继承Envers类,也没有任何好转。确保org.hibernate.integrator.spi.integrator在应用程序中

问题不是来自应用程序,而是来自代码本身。我们的entites有一个字段lastUpdateDate,它在每次合并时设置为当前日期。合并后,比较由envers完成,因此自上次修订以来,此字段已更改

对于那些好奇的人来说,版本之间的更改在org.hibernate.envers.internal.entities.mapper.MultiPropertyMapper.map中进行评估,至少在evers 4.3.5.Final上进行评估,如果oldState和newState之间有任何更改,则返回true。它根据所比较的属性使用特定的映射器

编辑:我将在这里说明我是如何解决这个问题的,但也可以使用达格玛的解决方案。然而,我的可能有点棘手和肮脏

我使用了Envers的EnversPostUpdateEventListenerImpl,如和中所述:我创建了我的Envers,并强制Envers使用它

@Override
public void onPostUpdate(PostUpdateEvent event) {
    //Maybe you should try catch that !
    if ( event.getOldState() != null ) {
        final EntityPersister entityPersister = event.getPersister();
        final String[] propertiesNames = entityPersister.getPropertyNames();

        for ( int i = 0; i < propertiesNames.length; ++i ) {
            String propertyName = propertiesNames[i];
            if(checkProperty(propertyName){
                event.getOldState()[i] = event.getState()[i];
        }
    }
    // Normal Envers processing
    super.onPostUpdate(event);
}

注意:您必须重新实现所有事件侦听器,即使它们只是继承Envers类,也没有任何好转。确保org.hibernate.integrator.spi.integrator在您的应用程序中

好消息是hibernate Envers按预期工作-除非修改可审核属性,否则不会创建AUD表中的版本条目

然而,在我们的应用程序中,我们实现了一个MergeEventListener,它在每个实体保存上更新跟踪字段lastUpdated、lastUpdatedBy。这导致Envers即使在实体没有更改的情况下也会创建一个新版本

最终,解决方案对我们来说非常简单——使用一个如何使用Hibernate中的拦截器和事件的示例:

我们将实现PersistEventListener和MergeEventListener的类替换为扩展EmptyInterceptor并重写onFlushDirty和onSave方法的类

public class EntitySaveInterceptor extends EmptyInterceptor {

  @Override
  public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
    setModificationTrackerProperties(entity);
    return super.onFlushDirty(entity, id, currentState, previousState, propertyNames, types);
  }

  @Override
  public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
    setModificationTrackerProperties(entity);
    return super.onSave(entity, id, state, propertyNames, types);
  }

  private void setModificationTrackerProperties(Object object) {
    if (SecurityContextHolder.getContext() != null && SecurityContextHolder.getContext().getAuthentication() != null) {
      Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
      if (principal != null && principal instanceof MyApplicationUserDetails) {
        User user = ((MyApplicationUserDetails) principal).getUser();
        if (object instanceof ModificationTracker && user != null) {
          ModificationTracker entity = (ModificationTracker) object;
          Date currentDateTime = new Date();
          if (entity.getCreatedDate() == null) {
            entity.setCreatedDate(currentDateTime);
          }
          if (entity.getCreatedBy() == null) {
            entity.setCreatedBy(user);
          }
          entity.setLastUpdated(currentDateTime);
          entity.setLastUpdatedBy(user);
        }
      }
    }
  }
}
将EntitySaveInterceptor连接到Hibernate JPA持久化单元

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

    <persistence-unit name="myapplication" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <properties>
            <property name="hibernate.ejb.interceptor" value="org.myapplication.interceptor.EntitySaveInterceptor" />           
            <property name="hibernate.hbm2ddl.auto" value="none"/>
            <property name="hibernate.show_sql" value="false"/>
        </properties>
    </persistence-unit>

</persistence>

还可以通过使用PreUpdateEventListener的实现来设置ModificationTracker值来解决此问题,因为该侦听器也仅在对象脏时启动。

好消息是Hibernate Envers按预期工作-除非可审核的属性被修改

然而,在我们的应用程序中,我们实现了一个MergeEventListener,它在每个实体保存上更新跟踪字段lastUpdated、lastUpdatedBy。这导致Envers即使在实体没有更改的情况下也会创建一个新版本

最终,解决方案对我们来说非常简单——使用一个如何使用Hibernate中的拦截器和事件的示例:

我们将实现PersistEventListener和MergeEventListener的类替换为扩展EmptyInterceptor并重写onFlushDirty和onSave方法的类

public class EntitySaveInterceptor extends EmptyInterceptor {

  @Override
  public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
    setModificationTrackerProperties(entity);
    return super.onFlushDirty(entity, id, currentState, previousState, propertyNames, types);
  }

  @Override
  public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
    setModificationTrackerProperties(entity);
    return super.onSave(entity, id, state, propertyNames, types);
  }

  private void setModificationTrackerProperties(Object object) {
    if (SecurityContextHolder.getContext() != null && SecurityContextHolder.getContext().getAuthentication() != null) {
      Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
      if (principal != null && principal instanceof MyApplicationUserDetails) {
        User user = ((MyApplicationUserDetails) principal).getUser();
        if (object instanceof ModificationTracker && user != null) {
          ModificationTracker entity = (ModificationTracker) object;
          Date currentDateTime = new Date();
          if (entity.getCreatedDate() == null) {
            entity.setCreatedDate(currentDateTime);
          }
          if (entity.getCreatedBy() == null) {
            entity.setCreatedBy(user);
          }
          entity.setLastUpdated(currentDateTime);
          entity.setLastUpdatedBy(user);
        }
      }
    }
  }
}
将EntitySaveInterceptor连接到Hibernate JPA持久化单元

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

    <persistence-unit name="myapplication" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <properties>
            <property name="hibernate.ejb.interceptor" value="org.myapplication.interceptor.EntitySaveInterceptor" />           
            <property name="hibernate.hbm2ddl.auto" value="none"/>
            <property name="hibernate.show_sql" value="false"/>
        </properties>
    </persistence-unit>

</persistence>
也应该有可能解决这个问题
这个问题是通过使用PreUpdateEventListener的实现来设置ModificationTracker值来解决的,因为该侦听器也仅在对象变脏时被激发。

我也有类似的情况

我发现审计表中出现重复行的原因是在审计实体中使用了LocalDateTime字段

LocalDateTime字段被持久化为MySQL数据库中的DATETIME字段。问题是DATETIME字段的精度为1秒,而LocalDateTime的精度要高得多,因此当Envers将数据库中的数据与对象进行比较时,它会看到差异,即使LocalDateTime字段也没有更改


我通过将LocalDateTime字段截断为秒来解决这个问题。

我也遇到过类似的情况

我发现审计表中出现重复行的原因是在审计实体中使用了LocalDateTime字段

LocalDateTime字段被持久化为MySQL数据库中的DATETIME字段。问题是DATETIME字段的精度为1秒,而LocalDateTime的精度要高得多,因此当Envers将数据库中的数据与对象进行比较时,它会看到差异,即使LocalDateTime字段也没有更改


我通过将LocalDateTime字段截断为秒来解决此问题。

感谢您对自己问题的回答!我面临着一个类似的问题,并且在将更新与原始版本进行比较时遇到了问题。我使用Hibernate MergeEventListener设置跟踪列,但我的主要问题是MergeEvent原始对象与实体相同。也就是说,它已经应用了更新,因为它是一个托管对象。请分享你的解决方案好吗?很抱歉,你发表评论时我不在。我很高兴你找到了一个解决方案,我会编辑我的答案,包括我做了什么来纠正我的问题,但你的解决方案似乎也不错。谢谢你对自己问题的回答!我面临着一个类似的问题,并且在将更新与原始版本进行比较时遇到了问题。我使用Hibernate MergeEventListener设置跟踪列,但我的主要问题是MergeEvent原始对象与实体相同。也就是说,它已经应用了更新,因为它是一个托管对象。请分享你的解决方案好吗?很抱歉,你发表评论时我不在。我很高兴你找到了一个解决方案,我会编辑我的答案,包括我做了什么来纠正我的问题,但你的解决方案似乎也不错。谢谢你的想法和良好的描述!不过,感谢您的想法和良好的描述!然而