面试的时候遇到过这个问题,当时一脸懵逼。现在记录一下。。。
@Transactional失效场景
1. 在类内部调用调用类内部@Transactional标注的方法
1.1 定义一个错误的@Transactional标注实现,设置一个内部调用
@Service
public class UserServiceImpl extends ServiceImpl implements IUserService {
@Resource
UserMapper userMapper;
@Override
public void oneTest() {
oneTestNoPublic();
}
@Transactional(rollbackFor = Exception.class)
public void oneTestNoPublic(){
// 插入一条数据
int num = userMapper.insert(new User("小红", "青岛市", 18));
if (num > 0){
// 插入后制造一个错误
System.out.println(1 / 0);
}
// 再次插入一条数据
userMapper.insert(new User("小强", "烟台市", 21));
}
}
1.2 测试用例
@RestController
@RequestMapping("/transactional")
public class TransactionalTestController {
@Resource
IUserService userService;
/**
* 在类内部调用调用类内部@Transactional标注的方法
*/
@GetMapping("/one")
public void oneTest(){
try {
userService.oneTest();
} catch (Exception e){
e.printStackTrace();
}
}
}
1.3 运行后,控制台报错:
1.4 查看数据库,发现并没有回滚new User("小红", "青岛市", 18)这条数据
调用一个方法在类内部调用内部被@Transactional标注的事务方法,运行结果是事务不会正常开启。userMapper.insert(new User("小红", "青岛市", 18)) 操作没有进行回滚。
2. @Transactional注解标注方法修饰符为非public
2.1 新写一个TestServiceImpl,将@Transactional注解标注方法修饰符为非public
@Service
public class TestServiceImpl {
@Resource
UserMapper userMapper;
@Transactional(rollbackFor = Exception.class)
void twoTestNoPublic(){
// 插入一条数据
int num = userMapper.insert(new User("小红", "青岛市", 18));
if (num > 0){
// 插入后制造一个错误
System.out.println(1 / 0);
}
// 再次插入一条数据
userMapper.insert(new User("小强", "烟台市", 21));
}
}
@Service
public class UserServiceImpl extends ServiceImpl implements IUserService {
@Resource
TwoTestServiceImpl twoTestService;
@Override
public void twoTest() {
twoTestService.twoTestNoPublic();
}
}
2.2 测试用例
@RestController
@RequestMapping("/transactional")
public class TransactionalTestController {
@Resource
IUserService userService;
/**
* @Transactional注解标注方法修饰符为非public
*/
@GetMapping("/two")
public void twoTest(){
try {
userService.twoTest();
} catch (Exception e){
e.printStackTrace();
}
}
}
2.3 运行后,控制台报错:
2.4 查看数据库,发现并没有回滚new User("小红", "青岛市", 18)这条数据
以上的访问方式,导致事务没开启,因此在方法抛出异常时,userMapper.insert(new User("小红", "青岛市", 18)) 操作没有进行回滚。如果oneTestNoPublic()方法改为public的话将会正常开启事务,userMapper.insert(new User("小红", "青岛市", 18)) 将会进行回滚。
3. 事务方法内部捕捉了异常,且没有抛出新的异常
3.1 写一个内部捕捉异常,且不再抛出新的异常的方法
@Service
public class TestServiceImpl {
@Resource
UserMapper userMapper;
@Transactional(rollbackFor = Exception.class)
public void threeTestNoPublic(){
// 插入一条数据
int num = userMapper.insert(new User("小红", "青岛市", 18));
if (num > 0){
try {
// 插入后制造一个错误
System.out.println(1 / 0);
} catch (Exception e) {
e.printStackTrace();
return;
}
}
// 再次插入一条数据
userMapper.insert(new User("小强", "烟台市", 21));
}
}
3.2 测试用例
@RestController
@RequestMapping("/transactional")
public class TransactionalTestController {
@Resource
IUserService userService;
/**
* 事务方法内部捕捉了异常,且没有抛出新的异常
*/
@GetMapping("/three")
public void threeTest(){
try {
userService.threeTest();
} catch (Exception e){
e.printStackTrace();
}
}
}
3.3 运行后,控制台报错:
3.4 查看数据库,发现并没有回滚new User("小红", "青岛市", 18)这条数据
如果需要在catch里面回滚数据的话,就需要抛出新的异常或者在catch里面使用如下代码:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
好了,上面三种就是@Transactional注解不起作用,@Transactional注解失效的主要原因。