放弃 Hibernate Envers 中的修订

Posted

技术标签:

【中文标题】放弃 Hibernate Envers 中的修订【英文标题】:Discard revision in Hibernate Envers 【发布时间】:2017-05-29 07:38:55 【问题描述】:

在某些情况下我不需要为实体保存修订,但是如何处理呢?我只想跳过创建修订,已经审查过的源代码,没有机会。

如果您尝试在自定义 EnversPostInsertEventListenerImpl 中进行数据库更改,例如,在同一事务中进行调用(在 onPostInsert 中):

        PersistedProperty property = dao.findClass(PersistedProperty.class, "where propertyTemplate.id = ? and fileEntry.id = ?",
                propertyTemplate.getId(), f.getId());
        String v = convertObject2String(propertyTemplate, value);
        if (property == null) 
            property = new PersistedProperty(propertyTemplate, v, f);
         else 
            property.setValue(v);
        
        dao.merge(property);

然后你会得到下一个异常:

java.util.ConcurrentModificationException
 at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
 at java.util.ArrayList$Itr.next(ArrayList.java:851)
 at java.util.Collections$UnmodifiableCollection$1.next(Collections.java:1042)
 at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:587)
 at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:463)
 at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337)
 at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
 at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1435)
 at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:491)
 at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3201)
 at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2411)
 at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:467)
 at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:146)
 at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38)
 at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:220)
 at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:68)
 at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517)
 at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761)
 at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730)
 at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504)
 at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292)
 at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
 at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)

问题是 AbstractSaveEventListener 中相同 ActionQueue 实例的深度重复(第 318 行)以及内部事务对 ActionQueue 的最终修改。

【问题讨论】:

【参考方案1】:

在文档中,有一个名为 Conditional Auditing 的部分对此进行了讨论。

基本概念是您需要覆盖 Envers 注册的 Hibernate 事件侦听器,以使用您自己的自定义实现来处理触发审计。

您的实现基本上需要扩展 Envers 侦听器,并且:

    检查该事件是否适用于您有兴趣跳过的实体类型。 检查修改后的状态,看看是否只发生了暗示跳过的情况。 如果您想跳过,请不要委托给超级实现。

如果您使用ValidityAuditStrategy 而不是DefaultAuditStrategy,我强烈建议您避免对INSERT 操作使用条件审计。

据我所知,对 UPDATE 操作使用条件审计会起作用,但是在原始插入上这样做会导致永远不会添加 ADD 行,因此 ValidityAuditStrategy 将在首先UPDATE 您尝试审核该实体行。

Hibernate 6 的目标是超越用户操纵事件侦听器以进行条件审计的需要,并转向更类似于 JPA 事件侦听器注释的方法。详情请见HHH-11326。

【讨论】:

如何在自定义 EnversPostUpdateEventListenerImpl 中注入 Spring bean?这是hibernate中任何实例的典型问题。 您可以在调用持久层之前使用您定义的 ThreadLocal 变量,或者您可以公开 bean 的单例实例并以这种方式访问​​它。

以上是关于放弃 Hibernate Envers 中的修订的主要内容,如果未能解决你的问题,请参考以下文章

审计:对子修改的父实体修订(Javers/Envers/... + Hibernate)

Hibernate Envers 如何通过 EmbeddedId 的属性获取修订

如何强制 Hibernate Envers 在 Spring @Transactional 方法中提交修订

Hibernate Envers - 获取日期之间的修订和对象

Hibernate Envers - 添加历史数据

Hibernate Envers 禁用 RevisionListener