天天看點

對線面試官 @Transactional 注解下,事務失效的七種場景,通俗易懂

作者:碼農日常vlog

@Transactional是一種基于注解管理事務的方式,spring通過動态代理的方式為目标方法實作事務管理的增強。

@Transactional使用起來友善,但也需要注意引起@Transactional失效的場景,本文總結了七種情況,下面進行逐一分析。這些場景無論是開發還是面試都能用得上,簡介明了,通俗易懂

1、異常被捕獲後沒有抛出

當異常被捕獲後,并且沒有再抛出,那麼deleteUserA是不會復原的。

@Transactional
public void deleteUser() {
    userMapper.deleteUserA();
    try {
        int i = 1 / 0;
        userMapper.deleteUserB();
    } catch (Exception e) {
        e.printStackTrace();
    }
}           

2、抛出非運作時異常

異步雖然抛出了,但是抛出的是非RuntimeException類型的異常,依舊不會生效。

@Transactional
public void deleteUser() throws MyException{
    userMapper.deleteUserA();
    try {
        int i = 1 / 0;
        userMapper.deleteUserB();
    } catch (Exception e) {
        throw new MyException();
    }
}           

如果指定了復原異常類型為Exception,那麼就可以復原非RuntimeException類型異常了。

@Transactional(rollbackFor = Exception.class)

3、方法内部直接調用

如果先調用deleteUser(),那麼deleteUserA()是不會復原的,其原因就是@Transactional根本沒生成代理,如果直接調用deleteUser2()那麼沒問題,deleteUserA()會復原。

public void deleteUser() throws MyException{
    deleteUser2();
}

@Transactional
public void deleteUser2() throws MyException{
    userMapper.deleteUserA();
    int i = 1 / 0;
    userMapper.deleteUserB();
}
修改方式,把目前類自己注入一下調用即可。
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
 //自己注入自己
    @Autowired
    UserService userService;

 public void deleteUser() throws MyException{
     userService.deleteUser2();
 }

 @Transactional
 public void deleteUser2() throws MyException{
     userMapper.deleteUserA();
     int i = 1 / 0;
     userMapper.deleteUserB();
 }
}


           

4、新開啟一個線程

如下的方式deleteUserA()也不會復原,因為spring實作事務的原理是通過ThreadLocal把資料庫連接配接綁定到目前線程中,新開啟一個線程擷取到的連接配接就不是同一個了。

@Transactional
public void deleteUser() throws MyException{
    userMapper.deleteUserA();
 try {
  //休眠1秒,保證deleteUserA先執行
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    new Thread(() -> {
        int i = 1/0;
        userMapper.deleteUserB();
    }).start();    
}           

5、注解到private方法上

idea直接會給出提示Methods annotated with ‘@Transactional’ must be overridable ,原理很簡單,private修飾的方式,spring無法生成動态代理。

@Transactional
private void deleteUser() throws MyException{
    userMapper.deleteUserA();
    int i = 1/0;
    userMapper.deleteUserB();
}           

6、資料庫本身不支援

mysql資料庫,必須設定資料庫引擎為InnoDB。

7、事務傳播屬性設定錯誤

注意傳播屬性的設定,比如設定了:PROPAGATION_NOT_SUPPORIED(以非事務的方式執行,如果目前有事務則把目前事務挂起)。

轉載來源|blog.csdn.net/CSDN_WYL2016/article/details/106767583

繼續閱讀