溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java事務的詳細講解

發(fā)布時間:2021-09-16 20:28:50 來源:億速云 閱讀:201 作者:chen 欄目:編程語言

這篇文章主要介紹“java事務的詳細講解”,在日常操作中,相信很多人在java事務的詳細講解問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”java事務的詳細講解”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

事務概念

我們要理解下事務概念: 什么是事務呢?事務是并發(fā)控制的單位,是用戶定義的一個操作序列。有四個特性(ACID):

  • 原子性(Atomicity): 事務是數(shù)據(jù)庫的邏輯工作單位,事務中包括的諸操作要么全做,要么全不做。

  • 一致性(Consistency): 事務執(zhí)行的結(jié)果必須是使數(shù)據(jù)庫從一個一致性狀態(tài)變到另一個一致性狀態(tài)。一致性與原子性是密切相關的。

  • 隔離性(Isolation): 一個事務的執(zhí)行不能被其他事務干擾。

  • 持續(xù)性/永久性(Durability): 一個事務一旦提交,它對數(shù)據(jù)庫中數(shù)據(jù)的改變就應該是永久性的。

以上是書面解釋,簡單來說就是把你的操作統(tǒng)一化,要么所有操作都成功,要么就都不成功,如果執(zhí)行中有某一項操作失敗,其之前所有的操作都回滾到未執(zhí)行這一系列操作之前的狀態(tài)。

臟讀、不可重復讀、幻讀

先理解這三種由于并發(fā)訪問導致的數(shù)據(jù)讀取問題,再理解事務隔離級別就簡單多了。

臟讀

A事務讀取B事務尚未提交的數(shù)據(jù),此時如果B事務發(fā)生錯誤并執(zhí)行回滾操作,那么A事務讀取到的數(shù)據(jù)就是臟數(shù)據(jù)。就好像原本的數(shù)據(jù)比較干凈、純粹,此時由于B事務更改了它,這個數(shù)據(jù)變得不再純粹。這個時候A事務立即讀取了這個臟數(shù)據(jù),但事務B良心發(fā)現(xiàn),又用回滾把數(shù)據(jù)恢復成原來干凈、純粹的樣子,而事務A卻什么都不知道,最終結(jié)果就是事務A讀取了此次的臟數(shù)據(jù),稱為臟讀。

這種情況常發(fā)生于轉(zhuǎn)賬與取款操作中

java事務的詳細講解

不可重復讀(前后多次讀取,數(shù)據(jù)內(nèi)容不一致)

事務A在執(zhí)行讀取操作,由整個事務A比較大,前后讀取同一條數(shù)據(jù)需要經(jīng)歷很長的時間 。而在事務A第一次讀取數(shù)據(jù),比如此時讀取了小明的年齡為20歲,事務B執(zhí)行更改操作,將小明的年齡更改為30歲,此時事務A第二次讀取到小明的年齡時,發(fā)現(xiàn)其年齡是30歲,和之前的數(shù)據(jù)不一樣了,也就是數(shù)據(jù)不重復了,系統(tǒng)不可以讀取到重復的數(shù)據(jù),成為不可重復讀。

java事務的詳細講解

幻讀(前后多次讀取,數(shù)據(jù)總量不一致)

事務A在執(zhí)行讀取操作,需要兩次統(tǒng)計數(shù)據(jù)的總量,前一次查詢數(shù)據(jù)總量后,此時事務B執(zhí)行了新增數(shù)據(jù)的操作并提交后,這個時候事務A讀取的數(shù)據(jù)總量和之前統(tǒng)計的不一樣,就像產(chǎn)生了幻覺一樣,平白無故的多了幾條數(shù)據(jù),成為幻讀。

java事務的詳細講解

小總結(jié):不可重復讀和幻讀到底有什么區(qū)別?

(1) 不可重復讀是讀取了其他事務更改的數(shù)據(jù),針對update操作

解決:使用行級鎖,鎖定該行,事務A多次讀取操作完成后才釋放該鎖,這個時候才允許其他事務更改剛才的數(shù)據(jù)。

(2) 幻讀是讀取了其他事務新增的數(shù)據(jù),針對insert和delete操作

解決:使用表級鎖,鎖定整張表,事務A多次讀取數(shù)據(jù)總量之后才釋放該鎖,這個時候才允許其他事務新增數(shù)據(jù)。

這時候再理解事務隔離級別就簡單多了呢。

數(shù)據(jù)庫事務的隔離級別

SQL 標準定義的四種隔離級別被 ANSI(美國國家標準學會)和 ISO/IEC(國際標準)采用,每種級別對事務的處理能力會有不同程度的影響。事務是一系列的動作,它們綜合在一起才是一個完整的工作單元,這些動作必須全部完成,如果有一個失敗的話,那么事務就會回滾到最開始的狀態(tài),仿佛什么都沒發(fā)生過一樣。

數(shù)據(jù)庫事務的隔離級別有4個,由低到高依次為Read uncommitted 、Read committed 、Repeatable read 、Serializable ,這四個級別可以逐個解決臟讀 、不可重復讀 、幻讀 這幾類問題。

java事務的詳細講解

DEFAULT

默認值,表示使用底層數(shù)據(jù)庫的默認隔離級別。大部分數(shù)據(jù)庫為READ_COMMITTED(MySql默認REPEATABLE_READ)

READ UNCOMMITTED(讀未提交)

該隔離級別表示一個事務可以讀取另一個事務修改但還沒有提交的數(shù)據(jù)。該級別不能防止臟讀和不可重復讀,因此很少使用該隔離級別。

READ_COMMITTED (讀提交)

該隔離級別表示一個事務只能讀取另一個事務已經(jīng)提交的數(shù)據(jù)。該級別可以防止臟讀,這也是大多數(shù)情況下的推薦值。

REPEATABLE_READ (可重復讀)

該隔離級別表示一個事務在整個過程中可以多次重復執(zhí)行某個查詢,并且每次返回的記錄都相同。即使在多次查詢之間有新增的數(shù)據(jù)滿足該查詢,這些新增的記錄也會被忽略。該級別可以防止臟讀和不可重復讀。

SERIALIZABLE (串行化)

所有的事務依次逐個執(zhí)行,這樣事務之間就完全不可能產(chǎn)生干擾,也就是說,該級別可以防止臟讀、不可重復讀以及幻讀。但是這將嚴重影響程序的性能。通常情況下也不會用到該級別。 在該隔離級別下事務都是串行順序執(zhí)行的,MySQL 數(shù)據(jù)庫的 InnoDB 引擎會給讀操作隱式加一把讀共享鎖,從而避免了臟讀、不可重讀復讀和幻讀問題。

MVCC(多版本并發(fā)控制)

mysql中,默認的事務隔離級別是可重復讀(repeatable-read),為了解決不可重復讀,innodb采用了MVCC(多版本并發(fā)控制)來解決這一問題。 MVCC是利用在每條數(shù)據(jù)后面加了隱藏的兩列(創(chuàng)建版本號和刪除版本號),每個事務在開始的時候都會有一個遞增的版本號,用來和查詢到的每行記錄的版本號進行比較。 MYSQL MVCC

Spring事務傳播行為

先來介紹下Spring事務傳播行為的使用方法:

@Transactional(propagation=Propagation.REQUIRED)
public void test() {
        //todo something
}

注解@Transactional 通過使用 propagation 屬性設置,例如:@Transactional(propagation = Propagation.REQUIRED)

它的propagation屬性取值有以下幾種:

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

}

事務傳播行為:

  • REQUIRED :如果當前存在事務,則加入該事務;如果當前沒有事務,則創(chuàng)建一個新的事務。

  • SUPPORTS :如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續(xù)運行。

  • MANDATORY :如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。

  • REQUIRES_NEW :創(chuàng)建一個新的事務,如果當前存在事務,則把當前事務掛起。

  • NOT_SUPPORTED :以非事務方式運行,如果當前存在事務,則把當前事務掛起。

  • NEVER :以非事務方式運行,如果當前存在事務,則拋出異常。

  • NESTED :如果當前存在事務,則創(chuàng)建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價于 REQUIRED

Spring 事務的兩種實現(xiàn)

Spring 支持“編程式事務”管理和“聲明式事務”管理兩種方式:

1編程式事務: 編程式事務使用 TransactionTemplate 或者直接使用底層的 PlatformTransactionManager 實現(xiàn)事務。 對于編程式事務 Spring 比較推薦使用 TransactionTemplate 來對事務進行管理。

2聲明式事務: 聲明式事務是建立在 AOP 之上的。其本質(zhì)是對方法前后進行攔截,然后在目標方法開始之前創(chuàng)建或者加入一個事務,在執(zhí)行完目標方法之后根據(jù)執(zhí)行情況“提交”或者“回滾”事務。

兩種事務管理間的區(qū)別
  • 編程式事務允許用戶在代碼中精確定義事務的邊界。

  • 聲明式事務有助于用戶將操作與事務規(guī)則進行解耦,它是基于 AOP 交由 Spring 容器實現(xiàn),是開發(fā)人員只重點關注業(yè)務邏輯實現(xiàn)。

  • 編程式事務侵入到了業(yè)務代碼里面,但是提供了更加纖細的事務管理。而聲明式事務基于 AOP,所以既能起到事務作用,又可以不影響業(yè)務代碼的具體實現(xiàn)。一般而言比較推薦使用聲明式事務,尤其是使用 @Transactional 注解,它能很好地幫助開發(fā)者實現(xiàn)事務的同時,也減少代碼開發(fā)量,且使代碼看起來更加清爽整潔。

Spring 編程式事務

一般來說編程式事務有兩種方法可以實現(xiàn): 模板事務的方式(TransactionTemplate)平臺事務管理器方式(PlatformTransactionManager)

  • 模板事務的方式(TransactionTemplate): 主要是使用 TransactionTemplate 類實現(xiàn)事務,這也是 Spring 官方比較推薦的一種編程式使用方式;

例:

  • ① 獲取模板對象 TransactionTemplate;

  • ② 選擇事務結(jié)果類型;

  • ③ 業(yè)務數(shù)據(jù)操作處理;

  • ④ 業(yè)務執(zhí)行完成事務提交或者發(fā)生異常進行回滾;

其中 TransactionTemplate 的 execute 能接受兩種類型參數(shù)執(zhí)行事務,分別為:

 TransactionCallback<Object>(): 執(zhí)行事務且可以返回一個值。
 TransactionCallbackWithoutResult(): 執(zhí)行事務沒有返回值。

下面是使用 TransactionTemplate 的實例:

@Service
public class TransactionExample {
  /** 1、獲取 TransactionTemplate 對象 **/
  @Autowired
  private TransactionTemplate transactionTemplate;
  
  public void addUser() {
      // 2、使用 TransactionCallback 或者 TransactionCallbackWithoutResult 執(zhí)行事務
      transactionTemplate.execute(new TransactionCallbackWithoutResult() {
          @Override
          public void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
              try {
                  // 3、執(zhí)行業(yè)務代碼(這里進行模擬,執(zhí)行多個數(shù)據(jù)庫操作方法)
                  userMapper.delete(1);
                  userMapper.delete(2);
              } catch (Exception e) {
                  // 4、發(fā)生異常,進行回滾
                  transactionStatus.setRollbackOnly();
              }
          }
      });
  }
  
}

  • 平臺事務管理器方式(PlatformTransactionManager): 這里使用最基本的事務管理局對事務進行管理,借助 Spring 事務的 PlatformTransactionManager 及 TransactionDefinition 和 TransactionStatus 三個核心類對事務進行操作。

使用事務管理器方式實現(xiàn)事務步驟:

  • ① 獲取事務管理器 PlatformTransactionManager;

  • ② 獲取事務屬性定義對象 TransactionDefinition;

  • ③ 獲取事務狀態(tài)對象 TransactionStatus;

  • ④ 業(yè)務數(shù)據(jù)操作處理;

  • ⑤ 進行事務提交 commit 操作或者發(fā)生異常進行事務回滾 rollback 操作;

@Service
public class TransactionExample {
    
    /** 1、獲取 PlatformTransactionManager 對象 **/
    @Autowired
    private PlatformTransactionManager platformTransactionManager;

    public void addUser() {
        // 2、獲取默認事務定義
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        // 設置事務傳播行為
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // 3、根據(jù)事務定義對象設置的屬性,獲取事務狀態(tài)
        TransactionStatus status = platformTransactionManager.getTransaction(def);
        try {
            // 4、執(zhí)行業(yè)務代碼(這里進行模擬,執(zhí)行多個數(shù)據(jù)庫操作方法)
            userMapper.delete(1);
            userMapper.delete(2);
            // 5、事務進行提交
            platformTransactionManager.commit(status);
        } catch(Exception e){
            // 5、事務進行回滾
            platformTransactionManager.rollback(status);
        }
    }
    
}
Spring 聲明式事務

聲明式事務(declarative transaction management)顧名思義就是使用聲明的方式來處理事務。該方式是基于 Spring AOP 實現(xiàn)的,將具體業(yè)務邏輯和事務處理解耦,其本質(zhì)是在執(zhí)行方法前后進行攔截,在方法開始之前創(chuàng)建或者加入一個事務,在執(zhí)行完目標方法之后根據(jù)執(zhí)行情況提交或者回滾事務。

常用的聲明式事務使用方法

常用的聲明式事務使用方法有

  • 1 XML

  • 2 @Transactional 注解

兩種方法,由于近幾年 SpringBoot 的流行,提供很方便的自動化配置,致使 XML 方式已經(jīng)逐漸淘汰,比較推薦使用注解的方式

@Transactional 的作用范圍

注解 @Transactional 不僅僅可以添加在方法上面,還可以添加到類級別上,當注解放在類級別時,表示所有該類的公共方法都配置相同的事務屬性信息。如果類級別配置了 @transactional,方法級別也配置了 @transactional,應用程序會以方法級別的事務屬性信息來管理事務,換言之,方法級別的事務屬性信息會覆蓋類級別的相關配置。

@Transactional 注解中可配置參數(shù)
  • value: 事務管理器,此配置項是設置 Spring 容器中的 Bean 名稱,這個 Bean 需要實現(xiàn)接口 PlatformTransactionManager。

  • transactionManager: 事務管理器,該參數(shù)和 value 配置保持一致,是同一個東西。

  • isolation: 事務隔離級別,默認為 Isolation.DEFAULT 級別

  • propagation: 事務傳播行為,默認為 Propagation.REQUIRED

  • timeout: 事務超時時間,單位為秒,默認值為-1,當事務超時時會拋出異常,進行回滾操作。

  • readOnly: 是否開啟只讀事務,是否開啟只讀事務,默認 false

  • rollbackForClassName: 回滾事務的異常類名定義,同 rollbackFor,只是用類名定義。

  • noRollbackForClassName: 指定發(fā)生哪些異常名不回滾事務,參數(shù)為類數(shù)組,同 noRollbackFor,只是使用類的名稱定義。

  • rollbackFor: 回滾事務異常類定義,當方法中出異常,且異常類和該參數(shù)指定的類相同時,進行回滾操作,否則提交事務。

  • noRollbackFor: 指定發(fā)生哪些異常不回滾事務,當方法中出異常,且異常類和該參數(shù)指定的類相同時,不回滾而是將繼續(xù)提交事務。

示例
@Transactional(propagation=Propagation.REQUIRED)
public void test() {
        //todo something
}

注意: 一般而言,不推薦將 @Transaction 配置到類上,因為這樣很可能使后來的維護人員必須強制使用事務。

使用事務時需要注意的點

  • 1、遇到異常檢測不回滾,原因:默認RuntimeException級別才回滾,如果是Eexception級別的異常需要手動添加

@Transactional(rollbackFor=Exception.class)
  • 2、捕捉異常后事物不生效,原因:捕捉處理了異常導致框架無法感知異常,自然就無法回滾了。建議:若非實際業(yè)務要求,則在業(yè)務層統(tǒng)一拋出異常,然后在控制層統(tǒng)一處理

@Transactional(rollbackFor=Exception.class)
public void test() {
    try {
         //業(yè)務代碼
    } catch (Exception e) {
        // TODO: handle exception
    }
   //主動捕捉異常導致框架無法捕獲,從而導致事物失效
}

到此,關于“java事務的詳細講解”的學習就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關知識,請繼續(xù)關注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI