Hibernate多租户模式下Envers审计事务提交报错求助
Hibernate多租户+Envers审计事务提交失败:Negative revision numbers异常排查
问题场景
项目迁移至Hibernate多租户特性,通过以下方式注入EntityManager:
@Inject protected EntityManager em;
对应的生产者方法:
@Produces @Default @RequestScoped public EntityManager create() { String principalName = principal.getName(); String clientIdentifier = getClientIdentifier(principalName); MultiTenantResolver tenantResolver = (MultiTenantResolver) ((SessionFactoryImplementor) entityManagerFactory).getCurrentTenantIdentifierResolver(); tenantResolver.setTenantIdentifier(clientIdentifier); EntityManager entityManager = entityManagerFactory.createEntityManager(); logger.debug("=========== created entity manager " + entityManager); return entityManager; }
EntityManager创建正常,能为每个请求连接到对应租户数据库,但执行包含@Audited实体的事务时,提交失败,抛出异常:
Caused by: org.hibernate.envers.exception.AuditException: Negative revision numbers are not allowed at org.hibernate@6.2.6.Final//org.hibernate.envers.internal.revisioninfo.DefaultRevisionInfoGenerator.saveRevisionData(DefaultRevisionInfoGenerator.java:64) at org.hibernate@6.2.6.Final//org.hibernate.envers.internal.synchronization.AuditProcess.getCurrentRevisionData(AuditProcess.java:138) at org.hibernate@6.2.6.Final//org.hibernate.envers.internal.synchronization.AuditProcess.executeInSession(AuditProcess.java:115) at org.hibernate@6.2.6.Final//org.hibernate.envers.internal.synchronization.AuditProcess.doBeforeTransactionCompletion(AuditProcess.java:175) at org.hibernate@6.2.6.Final//org.hibernate.envers.internal.synchronization.AuditProcessManager$1.doBeforeTransactionCompletion(AuditProcessManager.java:47) at org.hibernate@6.2.6.Final//org.hibernate.engine.spi.ActionQueue$BeforeTransactionCompletionProcessQueue.beforeTransactionCompletion(ActionQueue.java:1010) at org.hibernate@6.2.6.Final//org.hibernate.engine.spi.ActionQueue.beforeTransactionCompletion(ActionQueue.java:548) at org.hibernate@6.2.6.Final//org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:1967) at org.hibernate@6.2.6.Final//org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:439) at org.hibernate@6.2.6.Final//org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl.beforeCompletion(JtaTransactionCoordinatorImpl.java:336) at org.hibernate@6.2.6.Final//org.hibernate.resource.transaction.backend.jta.internal.synchronization.SynchronizationCallbackCoordinatorNonTrackingImpl.beforeCompletion(SynchronizationCallbackCoordinatorNonTrackingImpl.java:47) at org.hibernate@6.2.6.Final//org.hibernate.resource.transaction.backend.jta.internal.synchronization.RegisteredSynchronization.beforeCompletion(RegisteredSynchronization.java:37) at org.jboss.as.transactions@29.0.1.Final//org.jboss.as.txn.service.internal.tsr.JCAOrderedLastSynchronizationList.beforeCompletion(JCAOrderedLastSynchronizationList.java:113) at org.wildfly.transaction.client@3.0.0.Final//org.wildfly.transaction.client.AbstractTransaction.performConsumer(AbstractTransaction.java:236) at org.wildfly.transaction.client@3.0.0.Final//org.wildfly.transaction.client.AbstractTransaction.performConsumer(AbstractTransaction.java:247) at org.wildfly.transaction.client@3.0.0.Final//org.wildfly.transaction.client.AbstractTransaction$AssociatingSynchronization.beforeCompletion(AbstractTransaction.java:292) at org.jboss.jts//com.arjuna.ats.internal.jta.resources.arjunacore.SynchronizationImple.beforeCompletion(SynchronizationImple.java:76) at org.jboss.jts//com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.beforeCompletion(TwoPhaseCoordinator.java:360) at org.jboss.jts//com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:91) at org.jboss.jts//com.arjuna.ats.arjuna.AtomicAction.commit(AtomicAction.java:162) at org.jboss.jts//com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1295) ... 125 more
排查方向
- 修正租户标识符的传递方式:当前代码直接修改全局
CurrentTenantIdentifierResolver的租户ID,存在线程安全问题,会导致多请求租户串用。正确做法是在创建EntityManager时通过参数传递租户ID:Map<String, Object> props = new HashMap<>(); props.put(AvailableSettings.MULTI_TENANT_IDENTIFIER, clientIdentifier); EntityManager entityManager = entityManagerFactory.createEntityManager(props);CurrentTenantIdentifierResolver的设计目的是自动解析当前租户(比如从线程上下文、请求头获取),而非手动设置全局值。 - 检查租户数据库的Revision表序列:默认的
REVINFO表(Envers审计版本表)主键使用自增或序列生成。若某个租户库的序列未初始化、被重置为负数,或自增字段起始值错误,会导致生成负的revision号。直接查询租户库的REVINFO表,确认主键字段的生成策略及当前序列值。 - 验证Envers与多租户的兼容性:确认使用的Hibernate 6.2.6版本中,Envers对当前多租户模式(数据库隔离/ schema隔离)的支持是否正常。若为schema隔离模式,需确保Envers正确使用租户对应的schema;若为数据库隔离,需确认每个租户库都已创建Envers所需的审计表。
- 检查线程上下文与租户绑定:排查是否存在请求线程复用(如线程池)导致租户上下文残留的情况,确保每个请求的EntityManager都绑定了正确的租户ID,且事务过程中租户上下文未丢失。
- 启用详细日志排查:开启
org.hibernate.envers和org.hibernate.multitenancy的DEBUG级别日志,观察事务提交时Envers使用的租户ID、Revision数据的保存过程、ID生成的具体步骤,定位异常发生时的上下文信息。
内容的提问来源于stack exchange,提问作者jmiguel77
相关产品推荐
相关产品推荐

