如何在@Transactional方法中手动强制提交? [英] How to manually force a commit in a @Transactional method?

查看:13454
本文介绍了如何在@Transactional方法中手动强制提交?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Spring / Spring-data-JPA,并发现自己需要在单元测试中手动强制提交。我的用例是我正在进行多线程测试,我必须使用在生成线程之前保留的数据。

I'm using Spring / Spring-data-JPA and find myself needing to manually force a commit in a unit test. My use case is that I am doing a multi-threaded test in which I have to use data that is persisted before the threads are spawned.

不幸的是,考虑到测试正在 @Transactional 事务中运行,即使 flush 也不会使生成的线程可以访问它。

Unfortunately, given that the test is running in a @Transactional transaction, even a flush does not make it accessible to the spawned threads.

   @Transactional   
   public void testAddAttachment() throws Exception{
        final Contract c1 = contractDOD.getNewTransientContract(15);
        contractRepository.save(c1);

        // Need to commit the saveContract here, but don't know how!                
        em.getTransaction().commit();

        List<Thread> threads = new ArrayList<>();
        for( int i = 0; i < 5; i++){
            final int threadNumber = i; 
            Thread t =  new Thread( new Runnable() {
                @Override
                @Transactional
                public void run() {
                    try {
                        // do stuff here with c1

                        // sleep to ensure that the thread is not finished before another thread catches up
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            });
            threads.add(t);
            t.start();
        }

        // have to wait for all threads to complete
        for( Thread t : threads )
            t.join();

        // Need to validate test results.  Need to be within a transaction here
        Contract c2 = contractRepository.findOne(c1.getId());
    }

我尝试过使用实体管理器,但收到错误消息时我这样做:

I've tried using the entity manager to, but get an error message when I do:

org.springframework.dao.InvalidDataAccessApiUsageException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead; nested exception is java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:293)
    at org.springframework.orm.jpa.aspectj.JpaExceptionTranslatorAspect.ajc$afterThrowing$org_springframework_orm_jpa_aspectj_JpaExceptionTranslatorAspect$1$18a1ac9(JpaExceptionTranslatorAspect.aj:33)

有没有办法提交交易并继续交易?我一直无法找到允许我调用 commit()的方法。

Is there any way to commit the transaction and continue it? I have been unable to find any method that allows me to call a commit().

推荐答案

在测试hibernate事件监听器时我有一个类似的用例,它只在commit上调用。

I had a similar use case during testing hibernate event listeners which are only called on commit.

解决方案是将代码保存到另一个用 REQUIRES_NEW 注释的方法中。 (在另一个类中)这样生成一个新事务,并在方法返回后发出flush / commit。

The solution was to wrap the code to be persistent into another method annotated with REQUIRES_NEW. (In another class) This way a new transaction is spawned and a flush/commit is issued once the method returns.

请记住,这可能会影响所有其他测试!因此,请相应地编写它们,或者您需要确保在测试运行后可以进行清理。

Keep in mind that this might influence all the other tests! So write them accordingly or you need to ensure that you can clean up after the test ran.

这篇关于如何在@Transactional方法中手动强制提交?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆