久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

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

 丹楓無跡 2021-07-02

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

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

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

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

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

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

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

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

萬一在這兩個操作之間突然出現(xiàn)錯誤比如銀行系統(tǒng)崩潰或者網(wǎng)絡(luò)故障,,導(dǎo)致小明余額減少而小紅的余額沒有增加,這樣就不對了,。事務(wù)就是保證這兩個關(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)的異常,比如銀行中可能為突然停電等等
    //如果沒有配置事務(wù)管理的話會造成,,小紅賬戶多了1000而小明賬戶沒有少錢
int i = 10 / 0;
//小王賬戶少1000
accountDao.reduceMoney(1000,xiaoming);
}
}

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

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

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

參考

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

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

這里再多提一下一個非常重要的知識點: MySQL 怎么保證原子性的,?

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

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

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

通過 TransactionTemplate或者TransactionManager手動管理事務(wù),,實際應(yīng)用中很少使用,但是對于你理解 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ù)管理

推薦使用(代碼侵入性最?。瑢嶋H是通過 AOP 實現(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 個接口如下:

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

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

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

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

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

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

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

PlatformTransactionManager接口中定義了三個方法:

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;
}

這里多插一嘴,。為什么要定義或者說抽象出來PlatformTransactionManager這個接口呢,?

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

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

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

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

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

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

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

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ù)的隔離級別,默認(rèn)值是 DEFAULT
    int getIsolationLevel();
    // 返回事務(wù)的超時時間,,默認(rèn)值為-1,。如果超過該時間限制但事務(wù)還沒有完成,則自動回滾事務(wù),。
    int getTimeout();
    // 返回是否為只讀事務(wù),,默認(rèn)值為 false
    boolean isReadOnly();

    @Nullable
    String getName();
}

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

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

PlatformTransactionManager.getTransaction(…)方法返回一個 TransactionStatus 對象,。

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

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

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

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

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

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

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

舉個例子,!

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

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定義中包括了如下幾個表示傳播行為的常量:

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;
    ......
}

不過如此,為了方便使用,,Spring 會相應(yīng)地定義了一個枚舉類: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

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

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

舉個例子:如果我們上面的aMethod()bMethod()使用的都是PROPAGATION_REQUIRED傳播行為的話,,兩者使用的就是同一個事務(wù),,只要其中一個方法回滾,整個事務(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)建一個新的事務(wù),,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起,。也就是說不管外部方法是否開啟事務(wù),,Propagation.REQUIRES_NEW修飾的內(nèi)部方法會新開啟自己的事務(wù),且開啟的事務(wù)相互獨(dú)立,,互不干擾,。

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

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)建一個事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運(yùn)行;如果當(dāng)前沒有事務(wù),,則該取值等價于TransactionDefinition.PROPAGATION_REQUIRED,。也就是說:

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

這里還是簡單舉個例子:

如果 aMethod() 回滾的話,bMethod()bMethod2()都要回滾,,而bMethod()回滾的話,,并不會造成 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)前沒有事務(wù),則拋出異常,。(mandatory:強(qiáng)制性)

這個使用的很少,,就不舉例子來說了。

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

  • TransactionDefinition.PROPAGATION_SUPPORTS: 如果當(dāng)前存在事務(wù),,則加入該事務(wù);如果當(dāng)前沒有事務(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)容請看這篇文章:《太難了~面試官讓我結(jié)合案例講講自己對 Spring 事務(wù)傳播行為的理解,。》

3.3.2 事務(wù)隔離級別

TransactionDefinition 接口中定義了五個表示隔離級別的常量:

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)地定義了一個枚舉類: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;
}

}

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

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

因為平時使用 MySQL 數(shù)據(jù)庫比較多,這里再多提一嘴,!

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

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

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

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

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

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

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

所謂事務(wù)超時,,就是指一個事務(wù)所允許執(zhí)行的最長時間,,如果超過該時間限制但事務(wù)還沒有完成,則自動回滾事務(wù),。在 TransactionDefinition 中以 int 的值來表示超時時間,,其單位是秒,默認(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();

}

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

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

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

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

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

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

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

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

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

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

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

@Transactional(rollbackFor= MyException.class)

3.4. @Transactional 注解使用詳解

1) @Transactional 的作用范圍

  1. 方法 :推薦將注解使用于方法上,,不過需要注意的是:該注解只能應(yīng)用到 public 方法上,,否則不生效。
  2.  :如果這個注解使用在類上的話,,表明該注解對該類中所有的 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 個我平時比較常用的):

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

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

面試中在問 AOP 的時候可能會被問到的一個問題,。簡單說下吧,!

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

多提一嘴:createAopProxy() 方法 決定了是使用 JDK 還是 Cglib 來做動態(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);
}
}
  .......
}

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

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

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

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

這是由于Spring AOP代理的原因造成的,因為只有當(dāng) @Transactional 注解的方法在類以外被調(diào)用的時候,,Spring 事務(wù)管理才生效,。

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

@Service
public class MyService {

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

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

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

  1. @Transactional 注解只有作用到 public 方法上事務(wù)才生效,,不推薦在接口上使用;
  2. 避免同一個類中調(diào)用 @Transactional 注解的方法,,這樣會導(dǎo)致事務(wù)失效,;
  3. 正確的設(shè)置 @Transactional 的 rollbackFor 和 propagation 屬性,否則事務(wù)可能會回滾失敗
  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 高級編程》
  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é)
來源:github

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

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多