前言
这事儿吧,机缘巧合。我的项目是用springboot搭建的,junit用的4.12。最近在进行RabbitMQ相关功能的测试,打算测下nginx对消息队列的负载和集群本身的可用,想着,如果client调用失败了,就把插入的数据回滚掉,于是就配置了注解事务,然后,坑就出现了。
状态介绍
注解的配置如下,很常规:
<!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- enable transaction annotation support -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="false"/>
测试类大概如下:
/**
* @author nature
* @create 2018-01-09 14:35
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class MessageQueueDaoTest {
@Test
@Transactional
//@Rollback(false)
public void testTransaction(){
//FblockContext.getBean(TransactionTest.class).test();//FblockContext是我自己封装的spring上下文,这个getBean就是spring的,TransactionTest类源码在后面
MessageQueueDao messageQueueDao= FblockContext.getBean(MessageQueueDao.class);
RabbitMQRecord record=new RabbitMQRecord();
record.setId(KeyWorker.nextId());
record.setRecordType(0);
record.setRemark("testTransaction单元测试");
messageQueueDao.insertRabbitMQRecord(record);
throw new MessageQueueTestException("异常出现,需要回滚。");
}
}
/**
* @author nature
* @create 2018-01-10 12:52
*/
@Component
public class TransactionTest {
@Transactional
public void test(){
MessageQueueDao messageQueueDao= FblockContext.getBean(MessageQueueDao.class);
RabbitMQRecord record=new RabbitMQRecord();
record.setId(KeyWorker.nextId());
record.setRecordType(0);
record.setRemark("testTransaction单元测试");
messageQueueDao.insertRabbitMQRecord(record);
throw new MessageQueueTestException("异常出现,需要回滚。");
}
}
问题描述
上述代码其实就是我最初的代码,在测试方法类上打了@Transactional,内部抛出异常,然后期望回滚。结果也如我所愿,确实数据库里没插进去。然后,我说试试不抛出异常的,能不能插进去吧。结果也没插进去。。。。见了鬼了。查了半天,我一直以为是我的事务配置不对,于是我写了TransactionTest类,结果表现都是正常的,有异常回滚,无异常不会滚。机缘巧合,让我带了junit关键字去查询,找到了罪魁祸首。
在junit中有个注解是@Rollback,这个,默认是true,也就是,只要是我配置了注解,就会回滚,不管有没有异常。这个注解可以设置成false,@Rollback(false),那就变成了不管有没有异常都不会滚——蛋好疼。于是做了下面一组测试,具体探明下具体的规则吧,毕竟在做测试,没时间根源码了,前面说了在MessageQueueDaoTest内操作数据库的情况,其实如果MessageQueueDaoTest不配置事务,TransactionTest里面打事务注解,就是我们期待的结果了,为了探明规则,测过的接下来就不试了,接下来的数据库操作都在,TransactionTest内,MessageQueueDaoTest通过获取bean获取实例。
测试
- MessageQueueDaoTest有事务注解,TransactionTest有事务注解,无异常,这个结果是回滚的,那有有异常的我们不试了,肯定也回滚。
- MessageQueueDaoTest有事务注解,TransactionTest无事务注解,无异常,这个结果也是回滚的,有异常的也就不试了,肯定也回滚。
只要外面异常,里面都回滚,那我们试试@Rollback(false)的呢。 - MessageQueueDaoTest有事务注解,@Rollback(false),TransactionTest有事务注解,无异常,不会滚
- MessageQueueDaoTest有事务注解,@Rollback(false),TransactionTest有事务注解,有异常,回滚了。但是,按照Rollback的尿性,应该不会滚呀。于是推测,在里面有事务注解,按照自己的规则,拦截了异常回滚了,没有轮到Rollback生效。为了证实我们的猜测,我们把异常挪到MessageQueueDaoTest中去抛出
- MessageQueueDaoTest有事务注解,@Rollback(false),TransactionTest有事务注解,MessageQueueDaoTest抛异常 ,没有回滚,与我们的猜测一致
结论
junit4的测试类中打事务注解,默认会按照@Rollback(true)来进行处理,无论如何都会回滚,里面调用的方法打不打事务注解已经不重要了。而@Rollback(false)之后,似乎,也只控制在该层方法中出现的异常,就好像是这一层没有打事务似的,而不会传播到嵌套的事务内。根据上面的推测,我怀疑@Rollback(true)时想要提交,里面的传播级别就必须变成会新开事务的,这个,我这次就不证实了。
此篇博客,今证明,在下填过此坑了。。。。好了,下一个