在非调试模式下,Spring @transaction在junit中无法正常工作

时间:2015-02-23 08:07:21

标签: java spring hibernate junit transactions

所有MyService方法都是事务性的。下面的junit测试,获取项目数,保存新项目,并获取项目数,以确保计数增加1。

public class MyTest extends ServiceTest{

    1. int countBefore = myService.getCount();    //return n
    2. myService.add(item);                       //item is really added to DB
    3. int countAfter  = myService.getCount();    //return n (sometimes n+1)
}

@Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED)
getCount(){…}

@Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.SERIALIZABLE)
add(){…}

@Ignore
@ContextConfiguration(locations = { "file:src/main/resources/xxx-context.xml", 
                                    "file:src/main/resources/xxx-data.xml", 
                                    "file:src/main/resources/xxx-services.xml"  })
@TransactionConfiguration(transactionManager = "txManager", defaultRollback = false)
@TestExecutionListeners( {  DependencyInjectionTestExecutionListener.class,
                            DirtiesContextTestExecutionListener.class,
                            TransactionalTestExecutionListener.class,
                            TestListener.class})

public class ServiceTest extends AbstractUT{

@Ignore
@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners( {TestListener.class})
public class AbstractUT{

调试时(3.)返回n + 1,这就是我想要的。但是在没有调试的情况下运行测试时我会得到n。

即使有时在运行测试时我得到n + 1,下次我得到n并且当比较两次执行之间的std输出时,它看起来完全一样。我启用了log4j.logger.org.springframework.transaction = TRACE,我可以看到:

Initializing transaction synchronization
Getting transaction for MyService.getCount
...
Completing transaction for MyService.getCount
Clearing transaction synchronization

...

Initializing transaction synchronization
Getting transaction for MyService.add
...
Completing transaction for MyService.add
Clearing transaction synchronization

...

Initializing transaction synchronization
Getting transaction for MyService.getCount
...
Completing transaction for MyService.getCount
Clearing transaction synchronization

所以交易一个接一个地执行,但是(3.)没有看到保存的项目怎么可能?

根据我的测试类设置事务管理:https://stackoverflow.com/a/28657650/353985

我怎样才能找到出错的地方? 谢谢!

4 个答案:

答案 0 :(得分:4)

有类似的问题,但在我的情况下,它没有回滚。您似乎忘了添加@Transactional。来自文档(link

交易管理

  

在TestContext框架中,事务由管理   TransactionalTestExecutionListener,默认配置,   即使你没有明确声明你的@TestExecutionListeners   考试班。但是,要启用对事务的支持,您必须这样做   在ApplicationContext中配置PlatformTransactionManager bean   通过@ContextConfiguration语义加载(更多细节   在下面提供)。另外,你必须声明Spring的   @Transactional注释在类或方法级别为您的   测试

以上是上述链接的示例。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TransactionConfiguration(transactionManager="txMgr", defaultRollback=false)
@Transactional
public class FictitiousTransactionalTest {

@BeforeTransaction
public void verifyInitialDatabaseState() {
    // logic to verify the initial state before a transaction is started
}

@Before
public void setUpTestDataWithinTransaction() {
    // set up test data within the transaction
}

@Test
// overrides the class-level defaultRollback setting
@Rollback(true)
public void modifyDatabaseWithinTransaction() {
    // logic which uses the test data and modifies database state
}

@After
public void tearDownWithinTransaction() {
    // execute "tear down" logic within the transaction
}

@AfterTransaction
public void verifyFinalDatabaseState() {
    // logic to verify the final state after transaction has rolled back
}

}

答案 1 :(得分:0)

我发现直到现在通过测试的解决方案是将断言置于afterTransaction方法

公共类MyTest扩展了ServiceTest {

@Test
public void test(){
    1. int countBefore = myService.getCount();    //return n
    2. myService.add(item);                       //item is really added to DB
}
@AfterTransaction
public void verifyFinalDatabaseState() {
    3. int countAfter  = myService.getCount();    //return n (sometimes n+1) 
                                                  //Now always return n+1
}

答案 2 :(得分:0)

由于当前正在运行的事务设置为test method level,因此您有两种选择:

  1. 您要么从测试方法中删除@Transactional,要么依赖服务方法@Transactional边界。这样当你打电话时:

    int countBefore = myService.getCount();
    myService.add(item);
    int countAfter  = myService.getCount();
    
  2. 每个服务调用都将在一个独立的事务中运行,就像它在运行时生产调用中发生的那样。

    1. 在添加项目之后刷新Hibernate会话:

      int countBefore = myService.getCount();
      myService.add(item);
      transactionTemplate.execute(new TransactionCallback<Void>() {
          @Override
          public Company doInTransaction(TransactionStatus transactionStatus) {
              entityManager.flush();
              return null;
          }
      });
      int countAfter  = myService.getCount();
      
    2. HQL / JPQL计数查询应该在AUTO刷新模式下触发刷新,但是an SQL native query doesn't flush the Session

答案 3 :(得分:0)

我会在评论中提出这个问题,但由于我的声誉不允许,我会尽力提供答案。

您可能正在使用和ORM缓存计数查询的结果。根据您的add / getCount方法的实现方式以及ORM和数据源的配置,在第二次调用getCount时,您可能会获得在第一次调用getCount时获得的缓存值。

但这并不能解释为什么在调试模式下你总能得到正确的结果。