Springboot下事務管理的簡單使用

關於事務管理的概念這裏就很少介紹了,在個人博客「JDBC事務之理論篇」中也有介紹。html

關於Spring的事務管理,主要是經過事務管理器來進行的。這裏看個Spring事務管理的接口圖:(來自博客https://www.cnblogs.com/yixianyixian/p/8372832.html)spring

大概就是TransactionDefinition接口的實現類對對事務的一些配置設置進行定義;PlatformTransaction接口的實現類,是對事務進行管理的,像commit啊rollback等操做;而後TransactionStatus接口的實現類,是得到事務的一些狀態的。數據庫

 

而後,對於事務管理,Spring提供了聲明式和編程式的方法。   所謂編程式,就是在業務邏輯代碼中精確地定位事務管理,而聲明式事務管理,是基於AOP的,也就是說能夠將事務管理代碼和業務邏輯代碼解耦。編程

 

 

在SpringBoot中使用Spring的事務管理很是簡單,至關於Spring聲明式事務管理,並且是註釋版的。數組

咱們只須要用兩個註解:併發

@EnableTransactionManagement

@Transactional

@EnableTransactionManagement是要加載SpringBoot啓動類上的,至關因而配置文件中的:app

<!-- 聲明式事務管理 配置事物的註解方式注入-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

 

而這個@Transaction是放在你要進行事務管理的類或者是方法上的。框架

在@Transaction註解中有一些屬性能夠設置:ide

@Transactional註解中經常使用參數說明測試

參數名稱

功能描述

readOnly

該屬性用於設置當前事務是否爲只讀事務,設置爲true表示只讀,false則表示可讀寫,默認值爲false。例如:@Transactional(readOnly=true)

rollbackFor

該屬性用於設置須要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,則進行事務回滾。例如:

指定單一異常類:@Transactional(rollbackFor=RuntimeException.class)

指定多個異常類:@Transactional(rollbackFor={RuntimeException.class, Exception.class})

rollbackForClassName

該屬性用於設置須要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,則進行事務回滾。例如:

指定單一異常類名稱:@Transactional(rollbackForClassName="RuntimeException")

指定多個異常類名稱:@Transactional(rollbackForClassName={"RuntimeException","Exception"})

noRollbackFor

該屬性用於設置不須要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,不進行事務回滾。例如:

指定單一異常類:@Transactional(noRollbackFor=RuntimeException.class)

指定多個異常類:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})

noRollbackForClassName

該屬性用於設置不須要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,不進行事務回滾。例如:

指定單一異常類名稱:@Transactional(noRollbackForClassName="RuntimeException")

指定多個異常類名稱:

@Transactional(noRollbackForClassName={"RuntimeException","Exception"})

propagation

該屬性用於設置事務的傳播行爲,具體取值可參考表6-7。

例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

isolation

該屬性用於設置底層數據庫的事務隔離級別,事務隔離級別用於處理多事務併發的狀況,一般使用數據庫的默認隔離級別便可,基本不須要進行設置

timeout

該屬性用於設置事務的超時秒數,默認值爲-1表示永不超時

這裏要講的也就設置的比較多的兩個:

首先是propagation。翻譯過來是傳播行爲。什麼意思呢?

 

咱們通常都是將事務設置在Service層 那麼當咱們調用Service層的一個方法的時候它可以保證咱們的這個方法中執行的全部的對數據庫的更新操做保持在一個事務中,在事務層裏面調用的這些方法要麼所有成功,要麼所有失敗。那麼事務的傳播特性也是從這裏提及的。
若是你在你的Service層的這個方法中,除了調用了Dao層的方法以外,還調用了本類的其餘的Service方法,那麼在調用其餘的Service方法的時候,這個事務是怎麼規定的呢,我必須保證我在我方法裏掉用的這個方法與我自己的方法處在同一個事務中,不然若是保證事物的一致性。事務的傳播特性就是解決這個問題的,「事務是會傳播的」在Spring中有針對傳播特性的多種配置咱們大多數狀況下只用其中的一種:PROPGATION_REQUIRED:這個配置項的意思是說當我調用service層的方法的時候開啓一個事務(具體調用那一層的方法開始建立事務,要看你的aop的配置),那麼在調用這個service層裏面的其餘的方法的時候,若是當前方法產生了事務就用當前方法產生的事務,不然就建立一個新的事務。這個工做使由Spring來幫助咱們完成的。

 

這裏作一個各個Propagation的彙總:

1: PROPAGATION_REQUIRED 
加入當前正要執行的事務不在另一個事務裏,那麼就起一個新的事務
 好比說,ServiceB.methodB的事務級別定義爲PROPAGATION_REQUIRED, 那麼因爲執行ServiceA.methodA的時候,
         ServiceA.methodA已經起了事務,這時調用ServiceB.methodB,ServiceB.methodB看到本身已經運行在ServiceA.methodA
的事務內部,就再也不起新的事務。而假如ServiceA.methodA運行的時候發現本身沒有在事務中,他就會爲本身分配一個事務。
 這樣,在ServiceA.methodA或者在ServiceB.methodB內的任何地方出現異常,事務都會被回滾。即便ServiceB.methodB的事務已經被
 提交,可是ServiceA.methodA在接下來fail要回滾,ServiceB.methodB也要回滾


2:   PROPAGATION_SUPPORTS
若是當前在事務中,即以事務的形式運行,若是當前再也不一個事務中,那麼就以非事務的形式運行
 這就跟日常用的普通非事務的代碼只有一點點區別了。不理這個,由於我也沒有以爲有什麼區別


3:   PROPAGATION_MANDATORY
必須在一個事務中運行。也就是說,他只能被一個父事務調用。不然,他就要拋出異常。


4:   PROPAGATION_REQUIRES_NEW
這個就比較繞口了。 好比咱們設計ServiceA.methodA的事務級別爲PROPAGATION_REQUIRED,ServiceB.methodB的事務級別爲PROPAGATION_REQUIRES_NEW,
 那麼當執行到ServiceB.methodB的時候,ServiceA.methodA所在的事務就會掛起,ServiceB.methodB會起一個新的事務,等待ServiceB.methodB的事務完成之後,
 他才繼續執行。他與PROPAGATION_REQUIRED 的事務區別在於事務的回滾程度了。由於ServiceB.methodB是新起一個事務,那麼就是存在
 兩個不一樣的事務。若是ServiceB.methodB已經提交,那麼ServiceA.methodA失敗回滾,ServiceB.methodB是不會回滾的。若是ServiceB.methodB失敗回滾,
 若是他拋出的異常被ServiceA.methodA捕獲,ServiceA.methodA事務仍然可能提交。


5:   PROPAGATION_NOT_SUPPORTED 
當前不支持事務。好比ServiceA.methodA的事務級別是PROPAGATION_REQUIRED ,而ServiceB.methodB的事務級別是PROPAGATION_NOT_SUPPORTED ,
 那麼當執行到ServiceB.methodB時,ServiceA.methodA的事務掛起,而他以非事務的狀態運行完,再繼續ServiceA.methodA的事務。


6:   PROPAGATION_NEVER 
不能在事務中運行。假設ServiceA.methodA的事務級別是PROPAGATION_REQUIRED, 而ServiceB.methodB的事務級別是PROPAGATION_NEVER ,
 那麼ServiceB.methodB就要拋出異常了。


7:   PROPAGATION_NESTED 
理解Nested的關鍵是savepoint。他與PROPAGATION_REQUIRES_NEW的區別是,PROPAGATION_REQUIRES_NEW另起一個事務,將會與他的父事務相互獨立,
 而Nested的事務和他的父事務是相依的,他的提交是要等和他的父事務一塊提交的。也就是說,若是父事務最後回滾,他也要回滾的。
 而Nested事務的好處是他有一個savepoint。

 

通常用的較多就這個PROPAGATION_REQUIRED

 

而後還要講的要給屬性是: Isolation,就是確認隔離機制。

這個就和JDBC中的隔離機制基本一致,是爲了防止drity reads、non-repeatable reads、phantom reads而有的機制,隔離級別常量這裏也不細講了,和個人博客中記錄的「JDBC事務管理理論篇」中的基本同樣。

 

好的下面上代碼來看例子:

在咱們的service層中的一個類中新建一個測試方法:

先在接口中添加聲明:

public interface UserService {
    Map<String, Object> selectList(Map<String, Object> map);
    Map<String, Object> insert(User user);
    Map<String, Object> update(User user);
    Map<String, Object> delete(String id);
    void transactionTest(User user1, User user2);
}

 

再看實現類:(其餘方法的實現省略了)


@Service
public class UserServiceImpl implements UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    
    
    @Override
    public void transactionTest(User user1, User user2) {
        // TODO Auto-generated method stub
        userMapper.insert(user1);
        System.out.println(1/0);
        userMapper.insert(user2);
    }
    
}

 

而後在咱們service的新建方法中,咱們兩次調用userMapper的insert方法,也就是說進行了兩次數據庫的插入操做。而且在中間人爲製造了一個異常。

先來看沒進行事務管理的效果:

測試類:


@RunWith(SpringRunner.class)    // SpringJUnit支持,由此引入Spring-Test框架支持! 
@SpringBootTest             //提供spring環境
public class TransactionTest {
    
    @Autowired
    private UserService userService;
    
    @Test
    public void testTransaction() {
        User user1 = new User();
        User user2 = new User();
        
        user1.setId("111111");
        user1.setUserAccount("tt");
        user1.setUserName("user1");
        user1.setUserPassword("12312312");
        
        user2.setId("222222");
        user2.setUserAccount("bb");
        user2.setUserName("user2");
        user2.setUserPassword("12312312");
        
        userService.transactionTest(user1, user2);
    }
    
}

 

結果截圖:

運行前的數據:

運行後:

可見,執行了一個insert以後,由於異常,後面那個insert就沒執行了。

 

好的如今咱們加入事務管理:

加入啓動事務管理註解:

@SpringBootApplication(scanBasePackages={"com.stuPayment.*"})
@EnableTransactionManagement
@MapperScan("com.stuPayment.dao")
public class StuPaymentApplication {

    public static void main(String[] args) {
        SpringApplication.run(StuPaymentApplication.class, args);
    }
}

 

加入Transactional註解:

能夠加在類上,這樣的話,就類中的全部方法都事務管理,這裏咱們加在方法上:

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor=Exception.class)
    @Override
    public void transactionTest(User user1, User user2) {
        // TODO Auto-generated method stub
        userMapper.insert(user1);
        System.out.println(1/0);
        userMapper.insert(user2);
    }

 

再運行看效果:

由於這個時候是REQUIRED的傳播級別,也就是說兩個方法都是一個事務的,只要有錯誤,就會回滾。

 

最後再說幾點注意的地方:

一、用 spring 事務管理器,由spring來負責數據庫的打開,提交,回滾.默認遇到運行期例外(throw new RuntimeException("註釋");)會回滾,即遇到不受檢查(unchecked)的例外時回滾;而遇到須要捕獲的例外(throw new Exception("註釋");)不會回滾,即遇到受檢查的例外(就是非運行時拋出的異常,編譯器會檢查到的異常叫受檢查例外或說受檢查異常)時,需咱們指定方式來讓事務回滾要想全部異常都回滾,要加上 @Transactional( rollbackFor={Exception.class,其它異常}) .若是讓unchecked例外不回滾: @Transactional(notRollbackFor=RunTimeException.class)


二、@Transactional 註解應該只被應用到 public 可見度的方法上。 若是你在 protected、private 或者 package-visible 的方法上使用 @Transactional 註解,它也不會報錯, 可是這個被註解的方法將不會展現已配置的事務設置。

 

三、Spring團隊的建議是你在具體的類(或類的方法)上使用 @Transactional 註解,而不要使用在類所要實現的任何接口上。你固然能夠在接口上使用 @Transactional 註解,可是這將只能當你設置了基於接口的代理時它才生效。由於註解是不能繼承的,這就意味着若是你正在使用基於類的代理時,那麼事務的設置將不能被基於類的代理所識別,並且對象也將不會被事務代理所包裝(將被確認爲嚴重的)。所以,請接受Spring團隊的建議而且在具體的類上使用 @Transactional 註解。

 

 

參考過的博客:

這兩個是介紹Spring的事務管理的綜述的

  https://www.cnblogs.com/yixianyixian/p/8372832.html

  https://blog.csdn.net/donggua3694857/article/details/69858827

這兩個是介紹傳播行爲的概念的,第一個講概念能夠,第二個的例子挺清晰:

  https://blog.csdn.net/wwh578867817/article/details/51736723

  https://blog.csdn.net/hsgao_water/article/details/52860380

SpringBoot中的事務管理:

  https://blog.csdn.net/wohaqiyi/article/details/72895983

@Transactional註解介紹:

  https://www.cnblogs.com/caoyc/p/5632963.html

相關文章
相關標籤/搜索