Spring :事務使用的注意事項

1.美圖

在這裏插入圖片描述

2.概述

2.1 @Transactional 註解儘量直接加在方法上

爲什麼:因爲@Transactional直接加在類或者接口上,@Transactional註解會對類或者接口裏面所有的public方法都有效(相當於所有的public方法都加上了@Transactional註解,而且註解帶的參數都是一樣的)。第一影響性能,可能有些方法我不需要@Transactional註解,第二方法不同可能@Transactional註解需要配置的參數也不同,比如有一個方法只是做查詢操作,那咱們可能需要配置Transactional註解的readOnly參數。所以強烈建議@Transactional註解直接添加的需要的方法上。

2.2 @Transactional 註解必須添加在public方法上,private、protected方法上是無效的

在使用@Transactional 的時候一定要記住,在private,protected方法上添加@Transactional 註解不會有任何效果。相當於沒加一樣。即使外部能調到protected的方法也無效。和沒有添加@Transactional一樣。

2.3 函數之間相互調用

關於有@Transactional的函數之間調用,會產生什麼情況。這裏咱們通過幾個例子來說明。

2.3.1 同一個類中函數相互調用

同一個類AClass中,有兩個函數aFunction、aInnerFunction。aFunction調用aInnerFunction。而且aFunction函數會被外部調用。

2.3.1.1 情況0

aFunction添加了@Transactional註解,aInnerFunction函數沒有添加。aInnerFunction拋異常。

public class AClass {

    @Transactional(rollbackFor = Exception.class)
    public void aFunction() {
        //todo: 數據庫操作A(增,刪,該)
        aInnerFunction(); // 調用內部沒有添加@Transactional註解的函數
    }

    private void aInnerFunction() {
        //todo: 操作數據B(做了增,刪,改 操作)
        throw new RuntimeException("函數執行有異常!");
    }

}

結果:兩個函數操作的數據都會回滾

2.3.1.2 情況2

情況1:兩個函數都添加了@Transactional註解。aInnerFunction拋異常。

public class AClass {

    @Transactional(rollbackFor = Exception.class)
    public void aFunction() {
        //todo: 數據庫操作A(增,刪,該)
        aInnerFunction(); // 調用內部沒有添加@Transactional註解的函數
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    private void aInnerFunction() {
        //todo: 操作數據B(做了增,刪,改 操作)
        throw new RuntimeException("函數執行有異常!");
    }

}

結果:同第一種情況一樣,兩個函數對數據庫操作都會回滾。因爲同一個類中函數相互調用的時候,內部函數添加@Transactional註解無效。@Transactional註解只有外部調用纔有效

2.3.1.3 情況3

情況2: aFunction不添加註解,aInnerFunction添加註解。aInnerFunction拋異常。

public class AClass {

    public void aFunction() {
        //todo: 數據庫操作A(增,刪,該)
        aInnerFunction(); // 調用內部沒有添加@Transactional註解的函數
    }

    @Transactional(rollbackFor = Exception.class)
    protected void aInnerFunction() {
        //todo: 操作數據B(做了增,刪,改 操作)
        throw new RuntimeException("函數執行有異常!");
    }

}

結果:兩個函數對數據庫的操作都不會回滾。因爲內部函數@Transactional註解添加和沒添加一樣

2.3.1.4 情況4

情況3:aFunction添加了@Transactional註解,aInnerFunction函數沒有添加。aInnerFunction拋異常,不過在aFunction裏面把異常抓出來了。

public class AClass {

    @Transactional(rollbackFor = Exception.class)
    public void aFunction() {
        //todo: 數據庫操作A(增,刪,該)
        try {
            aInnerFunction(); // 調用內部沒有添加@Transactional註解的函數
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void aInnerFunction() {
        //todo: 操作數據B(做了增,刪,改 操作)
        throw new RuntimeException("函數執行有異常!");
    }

}

結果:兩個函數裏面的數據庫操作都成功。事務回滾的動作發生在當有@Transactional註解函數有對應異常拋出時纔會回滾。(當然了要看你添加的@Transactional註解有沒有效)

2.3.2 不同類中函數相互調用

兩個類AClass、BClass。AClass類有aFunction、BClass類有bFunction。AClass類aFunction調用BClass類bFunction。最終會在外部調用AClass類的aFunction。

2.3.2.1 情況1

情況0:aFunction添加註解,bFunction不添加註解。bFunction拋異常。

@Service()
public class AClass {

    private BClass bClass;

    @Autowired
    public void setbClass(BClass bClass) {
        this.bClass = bClass;
    }

    @Transactional(rollbackFor = Exception.class)
    public void aFunction() {
        //todo: 數據庫操作A(增,刪,該)
        bClass.bFunction();
    }

}

@Service()
public class BClass {

    public void bFunction() {
        //todo: 數據庫操作A(增,刪,該)
        throw new RuntimeException("函數執行有異常!");
    }
}

結果:兩個函數對數據庫的操作都回滾了

2.3.2.2 情況2

情況1:aFunction、bFunction兩個函數都添加註解,bFunction拋異常。

@Service()
public class AClass {

    private BClass bClass;

    @Autowired
    public void setbClass(BClass bClass) {
        this.bClass = bClass;
    }

    @Transactional(rollbackFor = Exception.class)
    public void aFunction() {
        //todo: 數據庫操作A(增,刪,該)
        bClass.bFunction();
    }

}

@Service()
public class BClass {

    @Transactional(rollbackFor = Exception.class)
    public void bFunction() {
        //todo: 數據庫操作A(增,刪,該)
        throw new RuntimeException("函數執行有異常!");
    }
}

結果:兩個函數對數據庫的操作都回滾了。兩個函數裏面用的還是同一個事務。這種情況下,你可以認爲事務rollback了兩次。兩個函數都有異常

2.3.2.3 情況3

情況2:aFunction、bFunction兩個函數都添加註解,bFunction拋異常。aFunction抓出異常。

@Service()
public class AClass {

    private BClass bClass;

    @Autowired
    public void setbClass(BClass bClass) {
        this.bClass = bClass;
    }

    @Transactional(rollbackFor = Exception.class)
    public void aFunction() {
        //todo: 數據庫操作A(增,刪,該)
        try {
            bClass.bFunction();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

@Service()
public class BClass {

    @Transactional(rollbackFor = Exception.class)
    public void bFunction() {
        //todo: 數據庫操作A(增,刪,該)
        throw new RuntimeException("函數執行有異常!");
    }
}

結果:兩個函數數據庫操作都沒成功。而且還拋異常了

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only。

看打印出來的解釋也很好理解把。咱們也可以這麼理解,兩個函數用的是同一個事務。bFunction函數拋了異常,調了事務的rollback函數。事務被標記了只能rollback了。程序繼續執行,aFunction函數裏面把異常給抓出來了,這個時候aFunction函數沒有拋出異常,既然你沒有異常那事務就需要提交,會調事務的commit函數。而之前已經標記了事務只能rollback-only(以爲是同一個事務)。直接就拋異常了,不讓調了。

2.3.2.4 情況4

情況3:aFunction、bFunction兩個函數都添加註解,bFunction拋異常。aFunction抓出異常。這裏要注意bFunction函數@Transactional註解我們是有變化的,加了一個參數propagation = Propagation.REQUIRES_NEW,控制事務的傳播行爲。表明是一個新的事務。其實咱們情況3就是來解決情況2的問題的。

@Service()
public class AClass {

    private BClass bClass;

    @Autowired
    public void setbClass(BClass bClass) {
        this.bClass = bClass;
    }

    @Transactional(rollbackFor = Exception.class)
    public void aFunction() {
        //todo: 數據庫操作A(增,刪,該)
        try {
            bClass.bFunction();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

@Service()
public class BClass {

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void bFunction() {
        //todo: 數據庫操作A(增,刪,該)
        throw new RuntimeException("函數執行有異常!");
    }
}

結果:bFunction函數裏面的操作回滾了,aFunction裏面的操作成功了。有了前面情況2的理解。這種情況也很好解釋。兩個函數不是同一個事務了

3.小結

  1. 要知道@Transactional註解裏面每個屬性的含義。@Transactional註解屬性就是來控制事務屬性的。通過這些屬性來生成事務。

  2. 要明確我們添加的@Transactional註解會不會起作用。@Transactional註解在外部調用的函數上纔有效果,內部調用的函數添加無效,要切記。這是由AOP的特性決定的。

  3. 要明確事務的作用範圍,有@Transactional的函數調用有@Transactional的函數的時候,進入第二個函數的時候是新的事務,還是沿用之前的事務。稍不注意就會拋UnexpectedRollbackException異常。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章