多数据源Envers审计测试报错:Only one entity may be annotated with @RevisionEntity
使用Spring Boot 2.6.6 + Hibernate Core 5.6.7.Final + Hibernate Envers 5.6.7.Final实现实体审计,配置了PostgreSQL和Oracle双数据源,各自对应独立的RevisionEntity(继承自抽象类CustomRevisionEntity,扩展了user_name字段)。应用运行正常,双库的审计数据都能正确存储,但在使用@DataJpaTest编写测试类时遇到问题:
- 初始报错:
entities share the same JPA entity name: [REVINFO] which is not allowed! - 将其中一个表名改为
REV_INFO后,又报错:org.hibernate.MappingException: Only one entity may be annotated with @RevisionEntity!
尝试通过@EnableAutoConfiguration排除无效,因与其他服务关联过多。
应用运行时,两个数据源的配置(DatastoreConfigurationOracle和DatastoreConfigurationPostgres)各自维护独立的EntityManagerFactory,RevisionEntity分别归属不同的工厂,Envers在各自上下文内处理审计,不会冲突。
但@DataJpaTest默认会创建单一的EntityManagerFactory,并扫描所有JPA实体和配置类,导致两个@RevisionEntity被加载到同一个工厂中:
- 第一个错误是因为两个
RevisionEntity的JPA实体名(@Entity(name))重复; - 第二个错误是因为Envers要求同一个
EntityManagerFactory内只能存在一个标注@RevisionEntity的类。
方案1:测试时仅加载目标数据源的配置
在测试类中通过@Import指定要测试的数据源对应的配置类,确保只加载目标库的EntityManagerFactory和RevisionEntity,避免冲突。
比如测试PostgreSQL的仓库:
@ExtendWith(SpringExtension.class) @DataJpaTest @Import(DatastoreConfigurationPostgres.class) class RepositoryTest{ @Autowired Repository repository; @Test public void find() { // 测试逻辑 } }
如果测试Oracle的仓库,替换为@Import(DatastoreConfigurationOracle.class)即可。
方案2:为RevisionEntity设置唯一的JPA实体名
保持数据库表名不变的前提下,给两个RevisionEntity设置不同的JPA实体名(通过@Entity(name)),避免实体名重复冲突:
// Oracle的RevisionEntity @Data @Entity(name = "ORACLE_REVINFO") // 唯一实体名 @Table(name = "REVINFO") // 映射到数据库的REVINFO表 @RevisionEntity(CustomRevisionListener.class) public class LegacyRevisionEntity extends CustomRevisionEntity { }
// PostgreSQL的RevisionEntity @Data @Entity(name = "POSTGRES_REVINFO") // 唯一实体名 @Table(name = "REVINFO") // 映射到数据库的REVINFO表 @RevisionEntity(CustomRevisionListener.class) public class ExtensionRevisionEntity extends CustomRevisionEntity { }
该方案可解决实体名重复问题,但仍需配合方案1,确保测试时只加载目标数据源的配置,否则仍会触发Envers的@RevisionEntity唯一限制。
内容的提问来源于stack exchange,提问作者qazah

