在spring+spring mvc +mybatis中进行Junit单元测试时,事务的回滚情况。
一:存在事务传播的情况下的事务回滚
存在事务传播,即:在A类的方法中调用B类上的方法。本例是在UserServiceImpl类中的insertUser()方法调用UserServiceTestTransactionImpl类的insertUser()方法。
@Transactional注解默认的事务的传播行为REQUIRED。
导入javax.transaction.Transactional或者org.springframework.transaction.annotation.Transactional包的
@Transactional注解均可,但要使Spring包的@Transactional注解失效,不再使用@Rollback(false),直接删除@Transactional注解即可
测试代码如下:
UserServiceImpl类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring/spring-application.xml"})
@Transactional //在junit测试时,@Transactional注解默认不提交事务,即默认回滚事务,防止污染数据库
@Rollback(false)//关闭回滚
@Service
public class UserServiceImpl implements UserService {
private static final Logger logger = Logger.getLogger(UserServiceImpl.class);
@Autowired
private UserServiceTestTransactionImpl userServiceTestTransaction;
public void insertUser(User user) throws Exception{
userServiceTestTransaction.insertUser(user);
}
@Test
public void test() throws Exception {
logger.info("调用insert(User user)方法");
insertUser(new User(11, "格格"));
}
UserServiceTestTransactionImpl类:
@Transactional
@Service
public class UserServiceTestTransactionImpl {
@Autowired
private UserMapper userMapper;
public void insertUser(User user) throws Exception {
userMapper.inserUser(user);
//throw new RuntimeException();
throw new IOException();
}
}
情况一:只要在UserServiceImpl类上加上@Transactional注解,即开启事务
在测试中,无论UserServiceTestTransactionImpl类上有无@Transactional注解,UserServiceTestTransactionImpl类的insertUser()方法不抛出异常、抛出不可查异常RuntimeException及抛出可查异常IOException,这三种情况都会回滚事务,数据库不会插入数据。
情况二:在UserServiceImpl类上加上@Transactional注解的同时加上@Rollback(false),即不开启事务
@Rollback(false)注解,即关闭事务回滚,即使@Transactional注解默认的回滚失效
这种情况相当于注解@Transactional与@Rollback(false)均被注释掉了
在测试中,关闭回滚,UserServiceTestTransactionImpl类的insertUser()方法不抛出异常和抛出可查异常IOException,这两种情况的事务会提交,不再回滚事务,相应的数据库中会插入数据。抛出不可查异常RuntimeException,事务回滚,数据库不会插入数据。
二:不存在事务的传播行为情况下的事务回滚
不存在事务的传播,即:同一个类中方法相互调用,本例是在同一个UserServiceImpl类中,test()方法调用insertUser()方法。
测试代码如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring/spring-application.xml"})
@Transactional //在junit测试时,@Transactional注解默认不提交事务,即默认回滚事务,防止污染数据库
@Rollback(false)//关闭回滚
@Service
public class UserServiceImpl implements UserService {
private static final Logger logger = Logger.getLogger(UserServiceImpl.class);
@Autowired
private UserMapper userMapper;
public void insertUser(User user)throws Exception {
userMapper.inserUser(user);
throw new RuntimeException();
}
@Test
public void test() throws Exception {
logger.info("调用insert(User user)方法");
insertUser(new User(11, "么么"));
boolean exist = userMapper.exist(11);
System.out.println("exist: " + exist);
}
}
情况一:在类上只加上@Transactional注解
@Transactional注解,会默认回滚事务,防止污染数据库。
在测试中,不抛出异常、抛出不可查异常RuntimeException及抛出可查异常IOException,这三种情况都会回滚事务,数据库不会插入数据。
情况二:在类上加上@Transactional注解的同时加上@Rollback(false)
@Rollback(false)注解,即关闭事务回滚
在测试中,关闭回滚,与情况一相反,不抛出异常、抛出不可查异常RuntimeException及抛出可查异常IOException,这三种情况的事务会提交,不再回滚,相应的数据库中会插入数据。
总结:
在单元测试时,只要测试类开启了事务,所有的操作,无论抛出什么类型的异常,无论是否存在事务的传播行为,无论被调用的类上是否有@Transactional注解,还有无论是否捕获异常,都会回滚事务。
在单元测试时,测试类不开启事务,如果被调用的类上有@Transactional注解(开启事务),此时若被调用类的被调用方法发生不可查异常(RuntimeException及其子类或者error),则事务会回滚,其他操作不会回滚。 解释:虽然单元测试没有开启事务,但是被调用的类本身存在事务,所以会出现事务回滚的情况。
在单元测试时,测试类不开启事务,如果被调用的类不开启事务(即类上没有@Transactional注解),被调用类中的方法不抛出异常、抛出不可查异常RuntimeException及抛出可查异常IOException,这三种情况都不会回滚事务。解释:因为都不存在事务,自然不会有事务回滚。
因此,我们最好在测试类上开启事务,导入 javax.transaction.Transactional包或者org.springframework.transaction.annotation.Transactional的@Transactional注解,避免污染数据库。
注意:
解决Transactional注解不回滚
1、检查你方法是不是public的
2、你的异常类型是不是unchecked异常
如果我想check异常也想回滚怎么办,注解上面写明异常类型即可@Transactional(rollbackFor=Exception.class)
类似的还有norollbackFor,自定义不回滚的异常
3、数据库引擎要支持事务,如果是MySQL,注意表要使用支持事务的引擎,比如innodb,如果是myisam,事务是不起作用的
4、是否开启了对注解的解析 <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
5、spring是否扫描到你这个包,如下是扫描到org.test下面的包<context:component-scan base-package="org.test" ></context:component-scan>
6、检查是不是同一个类中的方法调用(如a方法调用同一个类中的b方法)
7、异常是不是被你catch住了