I encountered this problem during the interview, and I looked dazed at the time. Now record it. . .

@Transactional failure scenario

1. Call the @Transactional marked method inside the class

1.1 Define an incorrect @Transactional annotation implementation and set up an internal call
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> 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 Test case
@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 After running, the console reports an error:

image.png

1.4 Check the database and found that the new User ("Xiaohong", "Qingdao City", 18) data was not rolled back

image.png

Call a method to call the internal transaction method marked by @Transactional inside the class, the result of the operation is that the transaction will not start normally. userMapper.insert(new User("Xiaohong", "Qingdao City", 18)) operation is not rolled back.

2. The @Transactional annotation marks the method modifier as non-public

2.1 Write a new TestServiceImpl, mark the @Transactional annotation method modifier as non-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<UserMapper, User> implements IUserService {

    @Resource
    TwoTestServiceImpl twoTestService;

    @Override
    public void twoTest() {
        twoTestService.twoTestNoPublic();
    }
}
2.2 Test case
@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 After running, the console reports an error:

image.png

2.4 Check the database and found that the new User ("Xiaohong", "Qingdao City", 18) data was not rolled back

image.png

The above access method resulted in the transaction not being opened, so when the method throws an exception, the userMapper.insert(new User("Xiaohong", "Qingdao City", 18)) operation is not rolled back. If the oneTestNoPublic() method is changed to public, the transaction will be opened normally, and userMapper.insert(new User("Xiaohong", "Qingdao City", 18)) will be rolled back.

3. The exception is caught inside the transaction method, and no new exception is thrown

3.1 Write a method that catches the exception internally and no longer throws a new exception
@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 Test case
@RestController
@RequestMapping("/transactional")
public class TransactionalTestController {

    @Resource
    IUserService userService;

    /**
     * 事务方法内部捕捉了异常,且没有抛出新的异常
     */
    @GetMapping("/three")
    public void threeTest(){
        try {
            userService.threeTest();
        } catch (Exception e){
            e.printStackTrace();
        }
    }

}
3.3 After running, the console reports an error:

image.png

3.4 Check the database and found that the new User("Xiaohong", "Qingdao City", 18) data was not rolled back

image.png

If you need to roll back the data in the catch, you need to throw a new exception or use the following code in the catch: TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

Well, the above three are the main reasons why the @Transactional annotation does not work, and the @Transactional annotation fails.

Personal blog address:

http://www.zhouzhaodong.xyz/

Test code address:

https://gitee.com/zhouzhaodong/springboot/tree/master/transactional


周兆东
107 声望21 粉丝

一个java小白的成长之路。。。