乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      Spring事務(wù)總結(jié)

       丹楓無(wú)跡 2021-07-02

      1. 什么是事務(wù)?

      事務(wù)是邏輯上的一組操作,要么都執(zhí)行,要么都不執(zhí)行。

      我們系統(tǒng)的每個(gè)業(yè)務(wù)方法可能包括了多個(gè)原子性的數(shù)據(jù)庫(kù)操作,比如下面的 savePerson() 方法中就有兩個(gè)原子性的數(shù)據(jù)庫(kù)操作。這些原子性的數(shù)據(jù)庫(kù)操作是有依賴的,它們要么都執(zhí)行,要不就都不執(zhí)行。

      public void savePerson() {
      personDao.save(person);
      personDetailDao.save(personDetail);
      }
      

      另外,需要格外注意的是:事務(wù)能否生效數(shù)據(jù)庫(kù)引擎是否支持事務(wù)是關(guān)鍵。比如常用的 MySQL 數(shù)據(jù)庫(kù)默認(rèn)使用支持事務(wù)的innodb引擎。但是,如果把數(shù)據(jù)庫(kù)引擎變?yōu)?nbsp;myisam,那么程序也就不再支持事務(wù)了!

      事務(wù)最經(jīng)典也經(jīng)常被拿出來(lái)說(shuō)例子就是轉(zhuǎn)賬了。假如小明要給小紅轉(zhuǎn)賬 1000 元,這個(gè)轉(zhuǎn)賬會(huì)涉及到兩個(gè)關(guān)鍵操作就是:

      1. 將小明的余額減少 1000 元

      2. 將小紅的余額增加 1000 元。

      萬(wàn)一在這兩個(gè)操作之間突然出現(xiàn)錯(cuò)誤比如銀行系統(tǒng)崩潰或者網(wǎng)絡(luò)故障,導(dǎo)致小明余額減少而小紅的余額沒(méi)有增加,這樣就不對(duì)了。事務(wù)就是保證這兩個(gè)關(guān)鍵操作要么都成功,要么都要失敗。

      public class OrdersService {
      private AccountDao accountDao;
      
      public void setOrdersDao(AccountDao accountDao) {
      this.accountDao = accountDao;
      }
      
        @Transactional(propagation = Propagation.REQUIRED,
                      isolation = Isolation.DEFAULT, readOnly = false, timeout = -1)
      public void accountMoney() {
          //小紅賬戶多1000
      accountDao.addMoney(1000,xiaohong);
      //模擬突然出現(xiàn)的異常,比如銀行中可能為突然停電等等
          //如果沒(méi)有配置事務(wù)管理的話會(huì)造成,小紅賬戶多了1000而小明賬戶沒(méi)有少錢
      int i = 10 / 0;
      //小王賬戶少1000
      accountDao.reduceMoney(1000,xiaoming);
      }
      }
      

      另外,數(shù)據(jù)庫(kù)事務(wù)的 ACID 四大特性是事務(wù)的基礎(chǔ),下面簡(jiǎn)單來(lái)了解一下。

      2. 事物的特性(ACID)了解么?

      • 原子性(Atomicity): 一個(gè)事務(wù)(transaction)中的所有操作,或者全部完成,或者全部不完成,不會(huì)結(jié)束在中間某個(gè)環(huán)節(jié)。事務(wù)在執(zhí)行過(guò)程中發(fā)生錯(cuò)誤,會(huì)被回滾(Rollback)到事務(wù)開(kāi)始前的狀態(tài),就像這個(gè)事務(wù)從來(lái)沒(méi)有執(zhí)行過(guò)一樣。即,事務(wù)不可分割、不可約簡(jiǎn)。
      • 一致性(Consistency): 在事務(wù)開(kāi)始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫(kù)的完整性沒(méi)有被破壞。這表示寫入的資料必須完全符合所有的預(yù)設(shè)約束、觸發(fā)器、級(jí)聯(lián)回滾等。
      • 隔離性(Isolation): 數(shù)據(jù)庫(kù)允許多個(gè)并發(fā)事務(wù)同時(shí)對(duì)其數(shù)據(jù)進(jìn)行讀寫和修改的能力,隔離性可以防止多個(gè)事務(wù)并發(fā)執(zhí)行時(shí)由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致。事務(wù)隔離分為不同級(jí)別,包括未提交讀(Read uncommitted)、提交讀(read committed)、可重復(fù)讀(repeatable read)和串行化(Serializable)。
      • 持久性(Durability): 事務(wù)處理結(jié)束后,對(duì)數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會(huì)丟失。

      參考

      3. 詳談 Spring 對(duì)事務(wù)的支持

      再提醒一次:你的程序是否支持事務(wù)首先取決于數(shù)據(jù)庫(kù) ,比如使用 MySQL 的話,如果你選擇的是 innodb 引擎,那么恭喜你,是可以支持事務(wù)的。但是,如果你的 MySQL 數(shù)據(jù)庫(kù)使用的是 myisam 引擎的話,那不好意思,從根上就是不支持事務(wù)的。

      這里再多提一下一個(gè)非常重要的知識(shí)點(diǎn): MySQL 怎么保證原子性的?

      我們知道如果想要保證事務(wù)的原子性,就需要在異常發(fā)生時(shí),對(duì)已經(jīng)執(zhí)行的操作進(jìn)行回滾,在 MySQL 中,恢復(fù)機(jī)制是通過(guò) 回滾日志(undo log) 實(shí)現(xiàn)的,所有事務(wù)進(jìn)行的修改都會(huì)先先記錄到這個(gè)回滾日志中,然后再執(zhí)行相關(guān)的操作。如果執(zhí)行過(guò)程中遇到異常的話,我們直接利用 回滾日志 中的信息將數(shù)據(jù)回滾到修改之前的樣子即可!并且,回滾日志會(huì)先于數(shù)據(jù)持久化到磁盤上。這樣就保證了即使遇到數(shù)據(jù)庫(kù)突然宕機(jī)等情況,當(dāng)用戶再次啟動(dòng)數(shù)據(jù)庫(kù)的時(shí)候,數(shù)據(jù)庫(kù)還能夠通過(guò)查詢回滾日志來(lái)回滾將之前未完成的事務(wù)。

      3.1. Spring 支持兩種方式的事務(wù)管理

      1).編程式事務(wù)管理

      通過(guò) TransactionTemplate或者TransactionManager手動(dòng)管理事務(wù),實(shí)際應(yīng)用中很少使用,但是對(duì)于你理解 Spring 事務(wù)管理原理有幫助。

      使用TransactionTemplate 進(jìn)行編程式事務(wù)管理的示例代碼如下:

      @Autowired
      private TransactionTemplate transactionTemplate;
      public void testTransaction() {
      
              transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                  @Override
                  protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
      
                      try {
      
                          // ....  業(yè)務(wù)代碼
                      } catch (Exception e){
                          //回滾
                          transactionStatus.setRollbackOnly();
                      }
      
                  }
              });
      }
      

      使用 TransactionManager 進(jìn)行編程式事務(wù)管理的示例代碼如下:

      @Autowired
      private PlatformTransactionManager transactionManager;
      
      public void testTransaction() {
      
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
                try {
                     // ....  業(yè)務(wù)代碼
                    transactionManager.commit(status);
                } catch (Exception e) {
                    transactionManager.rollback(status);
                }
      }
      

      2)聲明式事務(wù)管理

      推薦使用(代碼侵入性最?。瑢?shí)際是通過(guò) AOP 實(shí)現(xiàn)(基于@Transactional 的全注解方式使用最多)。

      使用 @Transactional注解進(jìn)行事務(wù)管理的示例代碼如下:

      @Transactional(propagation=propagation.PROPAGATION_REQUIRED)
      public void aMethod {
        //do something
        B b = new B();
        C c = new C();
        b.bMethod();
        c.cMethod();
      }
      

      3.2. Spring 事務(wù)管理接口介紹

      Spring 框架中,事務(wù)管理相關(guān)最重要的 3 個(gè)接口如下:

      • PlatformTransactionManager: (平臺(tái))事務(wù)管理器,Spring 事務(wù)策略的核心。
      • TransactionDefinition: 事務(wù)定義信息(事務(wù)隔離級(jí)別、傳播行為、超時(shí)、只讀、回滾規(guī)則)。
      • TransactionStatus: 事務(wù)運(yùn)行狀態(tài)。

      我們可以把 PlatformTransactionManager 接口可以被看作是事務(wù)上層的管理者,而 TransactionDefinition 和 TransactionStatus 這兩個(gè)接口可以看作是事物的描述。

      PlatformTransactionManager 會(huì)根據(jù) TransactionDefinition 的定義比如事務(wù)超時(shí)時(shí)間、隔離級(jí)別、傳播行為等來(lái)進(jìn)行事務(wù)管理 ,而 TransactionStatus 接口則提供了一些方法來(lái)獲取事務(wù)相應(yīng)的狀態(tài)比如是否新事務(wù)、是否可以回滾等等。

      3.2.1. PlatformTransactionManager:事務(wù)管理接口

      Spring 并不直接管理事務(wù),而是提供了多種事務(wù)管理器 。Spring 事務(wù)管理器的接口是: PlatformTransactionManager 。

      通過(guò)這個(gè)接口,Spring 為各個(gè)平臺(tái)如 JDBC(DataSourceTransactionManager)、Hibernate(HibernateTransactionManager)、JPA(JpaTransactionManager)等都提供了對(duì)應(yīng)的事務(wù)管理器,但是具體的實(shí)現(xiàn)就是各個(gè)平臺(tái)自己的事情了。

      PlatformTransactionManager 接口的具體實(shí)現(xiàn)如下:

      PlatformTransactionManager接口中定義了三個(gè)方法:

      package org.springframework.transaction;
      
      import org.springframework.lang.Nullable;
      
      public interface PlatformTransactionManager {
          //獲得事務(wù)
          TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
          //提交事務(wù)
          void commit(TransactionStatus var1) throws TransactionException;
          //回滾事務(wù)
          void rollback(TransactionStatus var1) throws TransactionException;
      }
      
      

      這里多插一嘴。為什么要定義或者說(shuō)抽象出來(lái)PlatformTransactionManager這個(gè)接口呢?

      主要是因?yàn)橐獙⑹聞?wù)管理行為抽象出來(lái),然后不同的平臺(tái)去實(shí)現(xiàn)它,這樣我們可以保證提供給外部的行為不變,方便我們擴(kuò)展。我前段時(shí)間分享過(guò):“為什么我們要用接口?”

      3.2.2. TransactionDefinition:事務(wù)屬性

      事務(wù)管理器接口 PlatformTransactionManager 通過(guò) getTransaction(TransactionDefinition definition) 方法來(lái)得到一個(gè)事務(wù),這個(gè)方法里面的參數(shù)是 TransactionDefinition 類 ,這個(gè)類就定義了一些基本的事務(wù)屬性。

      那么什么是 事務(wù)屬性 呢?

      事務(wù)屬性可以理解成事務(wù)的一些基本配置,描述了事務(wù)策略如何應(yīng)用到方法上。

      事務(wù)屬性包含了 5 個(gè)方面:

      TransactionDefinition 接口中定義了 5 個(gè)方法以及一些表示事務(wù)屬性的常量比如隔離級(jí)別、傳播行為等等。

      package org.springframework.transaction;
      
      import org.springframework.lang.Nullable;
      
      public interface TransactionDefinition {
          int PROPAGATION_REQUIRED = 0;
          int PROPAGATION_SUPPORTS = 1;
          int PROPAGATION_MANDATORY = 2;
          int PROPAGATION_REQUIRES_NEW = 3;
          int PROPAGATION_NOT_SUPPORTED = 4;
          int PROPAGATION_NEVER = 5;
          int PROPAGATION_NESTED = 6;
          int ISOLATION_DEFAULT = -1;
          int ISOLATION_READ_UNCOMMITTED = 1;
          int ISOLATION_READ_COMMITTED = 2;
          int ISOLATION_REPEATABLE_READ = 4;
          int ISOLATION_SERIALIZABLE = 8;
          int TIMEOUT_DEFAULT = -1;
          // 返回事務(wù)的傳播行為,默認(rèn)值為 REQUIRED。
          int getPropagationBehavior();
          //返回事務(wù)的隔離級(jí)別,默認(rèn)值是 DEFAULT
          int getIsolationLevel();
          // 返回事務(wù)的超時(shí)時(shí)間,默認(rèn)值為-1。如果超過(guò)該時(shí)間限制但事務(wù)還沒(méi)有完成,則自動(dòng)回滾事務(wù)。
          int getTimeout();
          // 返回是否為只讀事務(wù),默認(rèn)值為 false
          boolean isReadOnly();
      
          @Nullable
          String getName();
      }
      

      3.2.3. TransactionStatus:事務(wù)狀態(tài)

      TransactionStatus接口用來(lái)記錄事務(wù)的狀態(tài) 該接口定義了一組方法,用來(lái)獲取或判斷事務(wù)的相應(yīng)狀態(tài)信息。

      PlatformTransactionManager.getTransaction(…)方法返回一個(gè) TransactionStatus 對(duì)象。

      TransactionStatus 接口接口內(nèi)容如下:

      public interface TransactionStatus{
          boolean isNewTransaction(); // 是否是新的事物
          boolean hasSavepoint(); // 是否有恢復(fù)點(diǎn)
          void setRollbackOnly();  // 設(shè)置為只回滾
          boolean isRollbackOnly(); // 是否為只回滾
          boolean isCompleted; // 是否已完成
      }
      

      3.3. 事務(wù)屬性詳解

      實(shí)際業(yè)務(wù)開(kāi)發(fā)中,大家一般都是使用 @Transactional 注解來(lái)開(kāi)啟事務(wù),很多人并不清楚這個(gè)參數(shù)里面的參數(shù)是什么意思,有什么用。為了更好的在項(xiàng)目中使用事務(wù)管理,強(qiáng)烈推薦好好閱讀一下下面的內(nèi)容。

      3.3.1. 事務(wù)傳播行為

      事務(wù)傳播行為是為了解決業(yè)務(wù)層方法之間互相調(diào)用的事務(wù)問(wèn)題

      當(dāng)事務(wù)方法被另一個(gè)事務(wù)方法調(diào)用時(shí),必須指定事務(wù)應(yīng)該如何傳播。例如:方法可能繼續(xù)在現(xiàn)有事務(wù)中運(yùn)行,也可能開(kāi)啟一個(gè)新事務(wù),并在自己的事務(wù)中運(yùn)行。

      舉個(gè)例子!

      我們?cè)?A 類的aMethod()方法中調(diào)用了 B 類的 bMethod() 方法。這個(gè)時(shí)候就涉及到業(yè)務(wù)層方法之間互相調(diào)用的事務(wù)問(wèn)題。如果我們的 bMethod()如果發(fā)生異常需要回滾,如何配置事務(wù)傳播行為才能讓 aMethod()也跟著回滾呢?這個(gè)時(shí)候就需要事務(wù)傳播行為的知識(shí)了,如果你不知道的話一定要好好看一下。

      Class A {
          @Transactional(propagation=propagation.xxx)
          public void aMethod {
              //do something
              B b = new B();
              b.bMethod();
          }
      }
      
      Class B {
          @Transactional(propagation=propagation.xxx)
          public void bMethod {
             //do something
          }
      }
      

      TransactionDefinition定義中包括了如下幾個(gè)表示傳播行為的常量:

      public interface TransactionDefinition {
          int PROPAGATION_REQUIRED = 0;
          int PROPAGATION_SUPPORTS = 1;
          int PROPAGATION_MANDATORY = 2;
          int PROPAGATION_REQUIRES_NEW = 3;
          int PROPAGATION_NOT_SUPPORTED = 4;
          int PROPAGATION_NEVER = 5;
          int PROPAGATION_NESTED = 6;
          ......
      }
      

      不過(guò)如此,為了方便使用,Spring 會(huì)相應(yīng)地定義了一個(gè)枚舉類:Propagation

      package org.springframework.transaction.annotation;
      
      import org.springframework.transaction.TransactionDefinition;
      
      public enum Propagation {
      
      REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
      
      SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
      
      MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
      
      REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
      
      NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
      
      NEVER(TransactionDefinition.PROPAGATION_NEVER),
      
      NESTED(TransactionDefinition.PROPAGATION_NESTED);
      
      private final int value;
      
      Propagation(int value) {
      this.value = value;
      }
      
      public int value() {
      return this.value;
      }
      
      }
      
      

      正確的事務(wù)傳播行為可能的值如下 :

      1.TransactionDefinition.PROPAGATION_REQUIRED

      使用的最多的一個(gè)事務(wù)傳播行為,我們平時(shí)經(jīng)常使用的@Transactional注解默認(rèn)使用就是這個(gè)事務(wù)傳播行為。如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。也就是說(shuō):

      1. 如果外部方法沒(méi)有開(kāi)啟事務(wù)的話,Propagation.REQUIRED修飾的內(nèi)部方法會(huì)新開(kāi)啟自己的事務(wù),且開(kāi)啟的事務(wù)相互獨(dú)立,互不干擾。
      2. 如果外部方法開(kāi)啟事務(wù)并且被Propagation.REQUIRED的話,所有Propagation.REQUIRED修飾的內(nèi)部方法和外部方法均屬于同一事務(wù) ,只要一個(gè)方法回滾,整個(gè)事務(wù)均回滾。

      舉個(gè)例子:如果我們上面的aMethod()bMethod()使用的都是PROPAGATION_REQUIRED傳播行為的話,兩者使用的就是同一個(gè)事務(wù),只要其中一個(gè)方法回滾,整個(gè)事務(wù)均回滾。

      Class A {
          @Transactional(propagation=propagation.PROPAGATION_REQUIRED)
          public void aMethod {
              //do something
              B b = new B();
              b.bMethod();
          }
      }
      
      Class B {
          @Transactional(propagation=propagation.PROPAGATION_REQUIRED)
          public void bMethod {
             //do something
          }
      }
      

      2.TransactionDefinition.PROPAGATION_REQUIRES_NEW

      創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。也就是說(shuō)不管外部方法是否開(kāi)啟事務(wù),Propagation.REQUIRES_NEW修飾的內(nèi)部方法會(huì)新開(kāi)啟自己的事務(wù),且開(kāi)啟的事務(wù)相互獨(dú)立,互不干擾。

      舉個(gè)例子:如果我們上面的bMethod()使用PROPAGATION_REQUIRES_NEW事務(wù)傳播行為修飾,aMethod還是用PROPAGATION_REQUIRED修飾的話。如果aMethod()發(fā)生異?;貪L,bMethod()不會(huì)跟著回滾,因?yàn)?nbsp;bMethod()開(kāi)啟了獨(dú)立的事務(wù)。但是,如果 bMethod()拋出了未被捕獲的異常并且這個(gè)異常滿足事務(wù)回滾規(guī)則的話,aMethod()同樣也會(huì)回滾,因?yàn)檫@個(gè)異常被 aMethod()的事務(wù)管理機(jī)制檢測(cè)到了。

      Class A {
          @Transactional(propagation=propagation.PROPAGATION_REQUIRED)
          public void aMethod {
              //do something
              B b = new B();
              b.bMethod();
          }
      }
      
      Class B {
          @Transactional(propagation=propagation.REQUIRES_NEW)
          public void bMethod {
             //do something
          }
      }
      

      3.TransactionDefinition.PROPAGATION_NESTED:

      如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來(lái)運(yùn)行;如果當(dāng)前沒(méi)有事務(wù),則該取值等價(jià)于TransactionDefinition.PROPAGATION_REQUIRED。也就是說(shuō):

      1. 在外部方法未開(kāi)啟事務(wù)的情況下Propagation.NESTEDPropagation.REQUIRED作用相同,修飾的內(nèi)部方法都會(huì)新開(kāi)啟自己的事務(wù),且開(kāi)啟的事務(wù)相互獨(dú)立,互不干擾。
      2. 如果外部方法開(kāi)啟事務(wù)的話,Propagation.NESTED修飾的內(nèi)部方法屬于外部事務(wù)的子事務(wù),外部主事務(wù)回滾的話,子事務(wù)也會(huì)回滾,而內(nèi)部子事務(wù)可以單獨(dú)回滾而不影響外部主事務(wù)和其他子事務(wù)。

      這里還是簡(jiǎn)單舉個(gè)例子:

      如果 aMethod() 回滾的話,bMethod()bMethod2()都要回滾,而bMethod()回滾的話,并不會(huì)造成 aMethod() 和bMethod()回滾。

      Class A {
          @Transactional(propagation=propagation.PROPAGATION_REQUIRED)
          public void aMethod {
              //do something
              B b = new B();
              b.bMethod();
              b.bMethod2();
          }
      }
      
      Class B {
          @Transactional(propagation=propagation.PROPAGATION_NESTED)
          public void bMethod {
             //do something
          }
          @Transactional(propagation=propagation.PROPAGATION_NESTED)
          public void bMethod2 {
             //do something
          }
      }
      

      4.TransactionDefinition.PROPAGATION_MANDATORY

      如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則拋出異常。(mandatory:強(qiáng)制性)

      這個(gè)使用的很少,就不舉例子來(lái)說(shuō)了。

      若是錯(cuò)誤的配置以下 3 種事務(wù)傳播行為,事務(wù)將不會(huì)發(fā)生回滾,這里不對(duì)照案例講解了,使用的很少。

      • TransactionDefinition.PROPAGATION_SUPPORTS: 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行。
      • TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。
      • TransactionDefinition.PROPAGATION_NEVER: 以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常。

      更多關(guān)于事務(wù)傳播行為的內(nèi)容請(qǐng)看這篇文章:《太難了~面試官讓我結(jié)合案例講講自己對(duì) Spring 事務(wù)傳播行為的理解?!?/a>

      3.3.2 事務(wù)隔離級(jí)別

      TransactionDefinition 接口中定義了五個(gè)表示隔離級(jí)別的常量:

      public interface TransactionDefinition {
          ......
          int ISOLATION_DEFAULT = -1;
          int ISOLATION_READ_UNCOMMITTED = 1;
          int ISOLATION_READ_COMMITTED = 2;
          int ISOLATION_REPEATABLE_READ = 4;
          int ISOLATION_SERIALIZABLE = 8;
          ......
      }
      

      和事務(wù)傳播行為這塊一樣,為了方便使用,Spring 也相應(yīng)地定義了一個(gè)枚舉類:Isolation

      public enum Isolation {
      
      DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),
      
      READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),
      
      READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),
      
      REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),
      
      SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);
      
      private final int value;
      
      Isolation(int value) {
      this.value = value;
      }
      
      public int value() {
      return this.value;
      }
      
      }
      

      下面我依次對(duì)每一種事務(wù)隔離級(jí)別進(jìn)行介紹:

      • TransactionDefinition.ISOLATION_DEFAULT :使用后端數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別,MySQL 默認(rèn)采用的 REPEATABLE_READ 隔離級(jí)別 Oracle 默認(rèn)采用的 READ_COMMITTED 隔離級(jí)別.
      • TransactionDefinition.ISOLATION_READ_UNCOMMITTED :最低的隔離級(jí)別,使用這個(gè)隔離級(jí)別很少,因?yàn)樗试S讀取尚未提交的數(shù)據(jù)變更,可能會(huì)導(dǎo)致臟讀、幻讀或不可重復(fù)讀
      • TransactionDefinition.ISOLATION_READ_COMMITTED : 允許讀取并發(fā)事務(wù)已經(jīng)提交的數(shù)據(jù),可以阻止臟讀,但是幻讀或不可重復(fù)讀仍有可能發(fā)生
      • TransactionDefinition.ISOLATION_REPEATABLE_READ : 對(duì)同一字段的多次讀取結(jié)果都是一致的,除非數(shù)據(jù)是被本身事務(wù)自己所修改,可以阻止臟讀和不可重復(fù)讀,但幻讀仍有可能發(fā)生。
      • TransactionDefinition.ISOLATION_SERIALIZABLE : 最高的隔離級(jí)別,完全服從 ACID 的隔離級(jí)別。所有的事務(wù)依次逐個(gè)執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說(shuō),該級(jí)別可以防止臟讀、不可重復(fù)讀以及幻讀。但是這將嚴(yán)重影響程序的性能。通常情況下也不會(huì)用到該級(jí)別。

      因?yàn)槠綍r(shí)使用 MySQL 數(shù)據(jù)庫(kù)比較多,這里再多提一嘴!

      MySQL InnoDB 存儲(chǔ)引擎的默認(rèn)支持的隔離級(jí)別是 REPEATABLE-READ(可重讀)。我們可以通過(guò)SELECT @@tx_isolation;命令來(lái)查看,MySQL 8.0 該命令改為SELECT @@transaction_isolation;

      mysql> SELECT @@tx_isolation;
      +-----------------+
      | @@tx_isolation  |
      +-----------------+
      | REPEATABLE-READ |
      +-----------------+
      
      

      這里需要注意的是:與 SQL 標(biāo)準(zhǔn)不同的地方在于 InnoDB 存儲(chǔ)引擎在 REPEATABLE-READ(可重讀) 事務(wù)隔離級(jí)別下使用的是 Next-Key Lock 鎖算法,因此可以避免幻讀的產(chǎn)生,這與其他數(shù)據(jù)庫(kù)系統(tǒng)(如 SQL Server)是不同的。所以說(shuō) InnoDB 存儲(chǔ)引擎的默認(rèn)支持的隔離級(jí)別是 REPEATABLE-READ(可重讀) 已經(jīng)可以完全保證事務(wù)的隔離性要求,即達(dá)到了 SQL 標(biāo)準(zhǔn)的 SERIALIZABLE(可串行化) 隔離級(jí)別。

      因?yàn)楦綦x級(jí)別越低,事務(wù)請(qǐng)求的鎖越少,所以大部分?jǐn)?shù)據(jù)庫(kù)系統(tǒng)的隔離級(jí)別都是 READ-COMMITTED(讀取提交內(nèi)容) :,但是你要知道的是 InnoDB 存儲(chǔ)引擎默認(rèn)使用 REPEATABLE-READ(可重讀) 并不會(huì)什么任何性能上的損失。

      更多關(guān)于事務(wù)隔離級(jí)別的內(nèi)容請(qǐng)看:

      1. 《一文帶你輕松搞懂事務(wù)隔離級(jí)別(圖文詳解)》
      2. 面試官:你說(shuō)對(duì) MySQL 事務(wù)很熟?那我問(wèn)你 10 個(gè)問(wèn)題

      3.3.3. 事務(wù)超時(shí)屬性

      所謂事務(wù)超時(shí),就是指一個(gè)事務(wù)所允許執(zhí)行的最長(zhǎng)時(shí)間,如果超過(guò)該時(shí)間限制但事務(wù)還沒(méi)有完成,則自動(dòng)回滾事務(wù)。在 TransactionDefinition 中以 int 的值來(lái)表示超時(shí)時(shí)間,其單位是秒,默認(rèn)值為-1。

      3.3.3. 事務(wù)只讀屬性

      package org.springframework.transaction;
      
      import org.springframework.lang.Nullable;
      
      public interface TransactionDefinition {
          ......
          // 返回是否為只讀事務(wù),默認(rèn)值為 false
          boolean isReadOnly();
      
      }
      

      對(duì)于只有讀取數(shù)據(jù)查詢的事務(wù),可以指定事務(wù)類型為 readonly,即只讀事務(wù)。只讀事務(wù)不涉及數(shù)據(jù)的修改,數(shù)據(jù)庫(kù)會(huì)提供一些優(yōu)化手段,適合用在有多條數(shù)據(jù)庫(kù)查詢操作的方法中。

      很多人就會(huì)疑問(wèn)了,為什么我一個(gè)數(shù)據(jù)查詢操作還要啟用事務(wù)支持呢?

      拿 MySQL 的 innodb 舉例子,根據(jù)官網(wǎng) https://dev./doc/refman/5.7/en/innodb-autocommit-commit-rollback.html 描述:

      MySQL 默認(rèn)對(duì)每一個(gè)新建立的連接都啟用了autocommit模式。在該模式下,每一個(gè)發(fā)送到 MySQL 服務(wù)器的sql語(yǔ)句都會(huì)在一個(gè)單獨(dú)的事務(wù)中進(jìn)行處理,執(zhí)行結(jié)束后會(huì)自動(dòng)提交事務(wù),并開(kāi)啟一個(gè)新的事務(wù)。

      但是,如果你給方法加上了Transactional注解的話,這個(gè)方法執(zhí)行的所有sql會(huì)被放在一個(gè)事務(wù)中。如果聲明了只讀事務(wù)的話,數(shù)據(jù)庫(kù)就會(huì)去優(yōu)化它的執(zhí)行,并不會(huì)帶來(lái)其他的什么收益。

      如果不加Transactional,每條sql會(huì)開(kāi)啟一個(gè)單獨(dú)的事務(wù),中間被其它事務(wù)改了數(shù)據(jù),都會(huì)實(shí)時(shí)讀取到最新值。

      分享一下關(guān)于事務(wù)只讀屬性,其他人的解答:

      1. 如果你一次執(zhí)行單條查詢語(yǔ)句,則沒(méi)有必要啟用事務(wù)支持,數(shù)據(jù)庫(kù)默認(rèn)支持 SQL 執(zhí)行期間的讀一致性;
      2. 如果你一次執(zhí)行多條查詢語(yǔ)句,例如統(tǒng)計(jì)查詢,報(bào)表查詢,在這種場(chǎng)景下,多條查詢 SQL 必須保證整體的讀一致性,否則,在前條 SQL 查詢之后,后條 SQL 查詢之前,數(shù)據(jù)被其他用戶改變,則該次整體的統(tǒng)計(jì)查詢將會(huì)出現(xiàn)讀數(shù)據(jù)不一致的狀態(tài),此時(shí),應(yīng)該啟用事務(wù)支持

      3.3.4. 事務(wù)回滾規(guī)則

      這些規(guī)則定義了哪些異常會(huì)導(dǎo)致事務(wù)回滾而哪些不會(huì)。默認(rèn)情況下,事務(wù)只有遇到運(yùn)行期異常(RuntimeException 的子類)時(shí)才會(huì)回滾,Error 也會(huì)導(dǎo)致事務(wù)回滾,但是,在遇到檢查型(Checked)異常時(shí)不會(huì)回滾。

      如果你想要回滾你定義的特定的異常類型的話,可以這樣:

      @Transactional(rollbackFor= MyException.class)
      

      3.4. @Transactional 注解使用詳解

      1) @Transactional 的作用范圍

      1. 方法 :推薦將注解使用于方法上,不過(guò)需要注意的是:該注解只能應(yīng)用到 public 方法上,否則不生效。
      2.  :如果這個(gè)注解使用在類上的話,表明該注解對(duì)該類中所有的 public 方法都生效。
      3. 接口 :不推薦在接口上使用。

      2) @Transactional 的常用配置參數(shù)

      @Transactional注解源碼如下,里面包含了基本事務(wù)屬性的配置:

      @Target({ElementType.TYPE, ElementType.METHOD})
      @Retention(RetentionPolicy.RUNTIME)
      @Inherited
      @Documented
      public @interface Transactional {
      
      @AliasFor("transactionManager")
      String value() default "";
      
      @AliasFor("value")
      String transactionManager() default "";
      
      Propagation propagation() default Propagation.REQUIRED;
      
      Isolation isolation() default Isolation.DEFAULT;
      
      int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
      
      boolean readOnly() default false;
      
      Class<? extends Throwable>[] rollbackFor() default {};
      
      String[] rollbackForClassName() default {};
      
      Class<? extends Throwable>[] noRollbackFor() default {};
      
      String[] noRollbackForClassName() default {};
      
      }
      

      @Transactional 的常用配置參數(shù)總結(jié)(只列巨額 5 個(gè)我平時(shí)比較常用的):

      屬性名 說(shuō)明
      propagation 事務(wù)的傳播行為,默認(rèn)值為 REQUIRED,可選的值在上面介紹過(guò)
      isolation 事務(wù)的隔離級(jí)別,默認(rèn)值采用 DEFAULT,可選的值在上面介紹過(guò)
      timeout 事務(wù)的超時(shí)時(shí)間,默認(rèn)值為-1(不會(huì)超時(shí))。如果超過(guò)該時(shí)間限制但事務(wù)還沒(méi)有完成,則自動(dòng)回滾事務(wù)。
      readOnly 指定事務(wù)是否為只讀事務(wù),默認(rèn)值為 false。
      rollbackFor 用于指定能夠觸發(fā)事務(wù)回滾的異常類型,并且可以指定多個(gè)異常類型。

      3)@Transactional 事務(wù)注解原理

      面試中在問(wèn) AOP 的時(shí)候可能會(huì)被問(wèn)到的一個(gè)問(wèn)題。簡(jiǎn)單說(shuō)下吧!

      我們知道,@Transactional 的工作機(jī)制是基于 AOP 實(shí)現(xiàn)的,AOP 又是使用動(dòng)態(tài)代理實(shí)現(xiàn)的。如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,默認(rèn)情況下會(huì)采用 JDK 的動(dòng)態(tài)代理,如果目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)了接口,會(huì)使用 CGLIB 動(dòng)態(tài)代理。

      多提一嘴:createAopProxy() 方法 決定了是使用 JDK 還是 Cglib 來(lái)做動(dòng)態(tài)代理,源碼如下:

      public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
      
      @Override
      public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
      if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
      throw new AopConfigException("TargetSource cannot determine target class: " +
      "Either an interface or a target is required for proxy creation.");
      }
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
      return new JdkDynamicAopProxy(config);
      }
      return new ObjenesisCglibAopProxy(config);
      }
      else {
      return new JdkDynamicAopProxy(config);
      }
      }
        .......
      }
      

      如果一個(gè)類或者一個(gè)類中的 public 方法上被標(biāo)注@Transactional 注解的話,Spring 容器就會(huì)在啟動(dòng)的時(shí)候?yàn)槠鋭?chuàng)建一個(gè)代理類,在調(diào)用被@Transactional 注解的 public 方法的時(shí)候,實(shí)際調(diào)用的是,TransactionInterceptor 類中的 invoke()方法。這個(gè)方法的作用就是在目標(biāo)方法之前開(kāi)啟事務(wù),方法執(zhí)行過(guò)程中如果遇到異常的時(shí)候回滾事務(wù),方法調(diào)用完成之后提交事務(wù)。

      TransactionInterceptor 類中的 invoke()方法內(nèi)部實(shí)際調(diào)用的是 TransactionAspectSupport 類的 invokeWithinTransaction()方法。由于新版本的 Spring 對(duì)這部分重寫很大,而且用到了很多響應(yīng)式編程的知識(shí),這里就不列源碼了。

      4)Spring AOP 自調(diào)用問(wèn)題

      若同一類中的其他沒(méi)有 @Transactional 注解的方法內(nèi)部調(diào)用有 @Transactional 注解的方法,有@Transactional 注解的方法的事務(wù)會(huì)失效。

      這是由于Spring AOP代理的原因造成的,因?yàn)橹挥挟?dāng) @Transactional 注解的方法在類以外被調(diào)用的時(shí)候,Spring 事務(wù)管理才生效。

      MyService 類中的method1()調(diào)用method2()就會(huì)導(dǎo)致method2()的事務(wù)失效。

      @Service
      public class MyService {
      
      private void method1() {
           method2();
           //......
      }
      @Transactional
       public void method2() {
           //......
        }
      }
      

      解決辦法就是避免同一類中自調(diào)用或者使用 AspectJ 取代 Spring AOP 代理。

      5) @Transactional 的使用注意事項(xiàng)總結(jié)

      1. @Transactional 注解只有作用到 public 方法上事務(wù)才生效,不推薦在接口上使用;
      2. 避免同一個(gè)類中調(diào)用 @Transactional 注解的方法,這樣會(huì)導(dǎo)致事務(wù)失效;
      3. 正確的設(shè)置 @Transactional 的 rollbackFor 和 propagation 屬性,否則事務(wù)可能會(huì)回滾失敗
      4. ......

      4. Reference

      1. [總結(jié)]Spring 事務(wù)管理中@Transactional 的參數(shù):http://www./spring 事務(wù)管理中 transactional 的參數(shù)/
      2. Spring 官方文檔:https://docs./spring/docs/4.2.x/spring-framework-reference/html/transaction.html
      3. 《Spring5 高級(jí)編程》
      4. 透徹的掌握 Spring 中@transactional 的使用: https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html
      5. Spring 事務(wù)的傳播特性:https://github.com/love-somnus/Spring/wiki/Spring 事務(wù)的傳播特性
      6. Spring 事務(wù)傳播行為詳解 :https://segmentfault.com/a/1190000013341344
      7. 全面分析 Spring 的編程式事務(wù)管理及聲明式事務(wù)管理:https://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/index.html

      作者:Snailclimb
      鏈接:Spring事務(wù)總結(jié)
      來(lái)源:github

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多