使用Hibernate Envers进行集成测试

使用Hibernate Envers进行集成测试,hibernate,jpa-2.0,integration-testing,hibernate-envers,Hibernate,Jpa 2.0,Integration Testing,Hibernate Envers,我正试图围绕一些审计实体构建一些测试。我的问题是envers只审核事务提交 我需要创建/编辑一些测试对象,提交事务,然后检查修订 使用envers进行集成测试的最佳方法是什么 更新:这里有一个我想要实现的非常糟糕的、不确定的测试类。我更愿意这样做,而不依赖于测试方法的顺序 首先在单个事务中创建帐户和帐户\事务。两个审核条目均适用于修订版1 Second在新事务中更新了account_事务。经审核的条目位于修订版2 第三,在修订版1中加载已审核的帐户并对其进行处理 @Transactional @

我正试图围绕一些审计实体构建一些测试。我的问题是envers只审核事务提交

我需要创建/编辑一些测试对象,提交事务,然后检查修订

使用envers进行集成测试的最佳方法是什么

更新:这里有一个我想要实现的非常糟糕的、不确定的测试类。我更愿意这样做,而不依赖于测试方法的顺序

首先在单个事务中创建帐户和帐户\事务。两个审核条目均适用于修订版1

Second在新事务中更新了account_事务。经审核的条目位于修订版2

第三,在修订版1中加载已审核的帐户并对其进行处理

@Transactional
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/testApplicationContext.xml"})
public class TestAuditing {

    @Autowired
    private AccountDao accountDao;

    @PersistenceContext
    private EntityManager entityManager;

    @Test
    @Rollback(false)
    public void first() {
        Account account = account("Test Account", "xxxxxxxx", "xxxxxx");

        AccountTransaction transaction = transaction(new Date(), Deposit, 100, "Deposit");
        account.setTransactions(newArrayList(transaction));

        accountDao.create(account);
    }

    @Test
    @Rollback(false)
    public void second() {
        Account account = accountDao.getById(1L);
        AccountTransaction transaction = account.getTransactions().get(0);
        transaction.setDescription("Updated Transaction");
        accountDao.update(account);
    }

    @Test
    public void third() {
        AuditReader reader = AuditReaderFactory.get(entityManager);

        List<Number> accountRevisions = reader.getRevisions(Account.class, 1L);
        //One revision [1]

        List<Number> transactionRevisions = reader.getRevisions(AccountTransaction.class, 1L);
        //Two revisions [1, 2]

        Account currentAccount = accountDao.getById(1L);
        Account revisionAccount = (Account) reader.createQuery().forEntitiesAtRevision(Account.class, 1).getSingleResult();

        System.out.println(revisionAccount);
    }
@Transactional
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(位置={“/testApplicationContext.xml”})
公共类测试审核{
@自动连线
私人帐户;
@持久上下文
私人实体管理者实体管理者;
@试验
@回滚(false)
首先公开无效(){
账户=账户(“测试账户”、“xxxxxxxx”、“xxxxxx”);
AccountTransaction交易=交易(新日期(),存款,100,“存款”);
account.setTransactions(newArrayList(transaction));
accountDao.create(account);
}
@试验
@回滚(false)
第二公共空间(){
Account=accountDao.getById(1L);
AccountTransaction事务=account.getTransactions().get(0);
交易。集合描述(“更新交易”);
accountDao.update(account);
}
@试验
第三公共空间(){
AuditReader=AuditReaderFactory.get(entityManager);
List accountRevisions=reader.getRevisions(Account.class,1L);
//一次修订[1]
List transactionRevisions=reader.getRevisions(AccountTransaction.class,1L);
//两次修订[1,2]
Account currentAccount=accountDao.getById(1L);
Account revisionAccount=(Account)reader.createQuery().ForentiesAtRevision(Account.class,1.getSingleResult();
系统输出打印项次(修订账户);
}

根据Tomasz的建议,我使用TransactionTemplate在每个dao操作后实现提交。没有类级别@Transactional注释

envers审计条目是在方法完成之前插入的,这正是我所需要的

@ContextConfiguration("testApplicationContext.xml")
public class TestAuditing extends AbstractJUnit4SpringContextTests {

    @Autowired
    private PlatformTransactionManager platformTransactionManager;

    @Autowired
    private PersonDao personDao;

    private TransactionTemplate template;

    @Before
    public void transactionTemplate() {
         template = new TransactionTemplate(platformTransactionManager);
    }

    @Test
    public void test() {
        Person person = createInTransaction(person("Karl", "Walsh", address("Middle of nowhere")), personDao);
        System.out.println(person);
    }

    private <T> T createInTransaction(final T object, final Dao<?, T> dao) {
        return template.execute(new TransactionCallback<T>() {
            public T doInTransaction(TransactionStatus transactionStatus) {
                dao.create(object);
                return object;
            }
        });
    }
}
@ContextConfiguration(“testApplicationContext.xml”)
公共类TestAuditing扩展了AbstractJUnit4SpringContextTests{
@自动连线
私有平台TransactionManager平台TransactionManager;
@自动连线
私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人私人;
私有交易模板;
@以前
公共作废事务模板(){
模板=新事务模板(platformTransactionManager);
}
@试验
公开无效测试(){
Person-Person=createInTransaction(Person(“卡尔”、“沃尔什”、地址(“偏僻之地”)),personDao;
系统输出打印项次(人);
}

private T createInTransaction(final T object,final Dao我是Spring事务测试支持的用户,该支持在测试完成时回滚测试,并且由于环境的设计,没有创建修订。我创建了一个黑客程序,似乎允许一个人“告诉”envers在事务提交之前手动完成其工作,但允许spring继续回滚

这些片段应该会有所帮助。 1.创建您自己的auditlistener,覆盖现有的envers audit listener。这允许访问单元测试可见的静态成员。可能有更好的方法,但它可以工作

public class AuditEventListenerForUnitTesting extends AuditEventListener {

   public static AuditConfiguration auditConfig;

   @Override
   public void initialize(Configuration cfg) {
      super.initialize(cfg);
      auditConfig = super.getVerCfg();
   }
}
修改persistence.xml以包含这个新的侦听器类,而不是envers提供的侦听器类

(如有必要,对其他侦听器重复此操作)

现在在“单元”测试中:


我之所以需要这个,是因为我有一些针对hibernate实体的JDBC查询加入到了版本表中。

另外两个解决方案对我不起作用,所以我使用了另一种方法,我只是创建一个新事务并强制提交。每次我需要一个新的修订,我都会再次这样做

@Autowired
@Qualifier("transactionManager")
private PlatformTransactionManager platformTransactionManager;

@Test
public void enversTest() throws Exception{
    Entity myEntity = new Entity();

    TransactionStatus status = platformTransactionManager.getTransaction(new DefaultTransactionDefinition());
    myEntity.setName("oldName");
    myEntity = entityDao.merge(myEntity);
    platformTransactionManager.commit(status); // creates a revision with oldname

    TransactionStatus newStatus = platformTransactionManager.getTransaction(new DefaultTransactionDefinition());
    myEntity.setName("newName");
    myEntity = entityDao.merge(myEntity);
    platformTransactionManager.commit(newStatus); // creates a new revision with newName
}

如果您使用<代码>事务(TrAcExchange Maung=“TraceActudio Mead”)<代码>,它可能会绕过提交,并将每个测试视为一个事务(因此不是在同一个测试中的多个时间版本)…< /P> < P>这是通过与Envest4.2.19.Engor(JPA 2)的修改而被强烈启发的。。此解决方案也不需要提交事务,这在我的案例中是一个要求

首先创建以下
org.hibernate.integrator.spi.integrator的实现,并将其添加到类路径中:

public class MyIntegrator implements Integrator {

  public static AuditConfiguration auditConfig;

  @Override
  public void integrate(Configuration configuration, SessionFactoryImplementor sessionFactory,
    SessionFactoryServiceRegistry serviceRegistry) {
    auditConfig = AuditConfiguration.getFor(configuration);
  }

  @Override
  public void integrate(MetadataImplementor metadata, SessionFactoryImplementor sessionFactory,
    SessionFactoryServiceRegistry serviceRegistry) {
    // NOP
  }

  @Override
  public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
    // NOP
  }

}
然后在
src/test/resources
下创建一个
META-INF/services/org.hibernate.integrator.spi.integrator
文件,并将integrator类的完全限定名称粘贴到其中

在测试中,调用DAO方法,刷新hibernate会话,完成后告诉Envers继续:

EventSource es = (EventSource) entityManager.getDelegate();
SessionImplementor si = entityManager.unwrap(SessionImplementor.class);
MyIntegrator.auditConfig.getSyncManager().get(es).doBeforeTransactionCompletion(si);
然后,您可以测试数据库的内容并最终回滚事务。

for Envers的更新版本
5.4.15.Final

protectedvoid flushEnvers()
{
final EventSource EventSource=entityManager.unwrap(EventSource.class);
最终会话Session=entityManager.unwrap(Session.class);
最终SessionImplementor SessionImplementor=entityManager.unwrap(SessionImplementor.class);
final EnversService service=session.getSessionFactory()
.getSessionFactoryOptions()
.getServiceRegistry()
.getService(EnversService.class);
service.getAuditProcessManager().get(eventSource)
.DobeForTransactionCompletion(sessionImplementor);
}

Check out-无耻的自我推销。谢谢你回复Tomasz,但我仍然不知道如何从你的博文中解决我的问题。我真的不知道
EventSource es = (EventSource) entityManager.getDelegate();
SessionImplementor si = entityManager.unwrap(SessionImplementor.class);
MyIntegrator.auditConfig.getSyncManager().get(es).doBeforeTransactionCompletion(si);