溫馨提示×

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

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

總結(jié)Spring中事務(wù)的使用、抽象機(jī)制及模擬Spring事務(wù)實(shí)現(xiàn)

發(fā)布時(shí)間:2021-10-22 17:13:35 來(lái)源:億速云 閱讀:121 作者:iii 欄目:數(shù)據(jù)庫(kù)

本篇內(nèi)容介紹了“總結(jié)Spring中事務(wù)的使用、抽象機(jī)制及模擬Spring事務(wù)實(shí)現(xiàn)”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

本文大綱如下:

總結(jié)Spring中事務(wù)的使用、抽象機(jī)制及模擬Spring事務(wù)實(shí)現(xiàn)

Spring事務(wù)應(yīng)用大綱

編程式事務(wù)

Spring提供了兩種編程式事務(wù)管理的方法

  •  使用 TransactionTemplate 或者 TransactionalOperator.

  •  直接實(shí)現(xiàn)TransactionManager接口

如果是使用的是命令式編程,Spring推薦使用TransactionTemplate 來(lái)完成編程式事務(wù)管理,如果是響應(yīng)式編程,那么使用TransactionalOperator更加合適。

TransactionTemplate

使用示例(我這里直接用的官網(wǎng)提供的例子了)

public class SimpleService implements Service {      private final TransactionTemplate transactionTemplate;       // 使用構(gòu)造對(duì)transactionTemplate進(jìn)行初始化      // 需要提供一個(gè)transactionManager      public SimpleService(PlatformTransactionManager transactionManager) {          this.transactionTemplate = new TransactionTemplate(transactionManager);      }      public Object someServiceMethod() {          return transactionTemplate.execute(new TransactionCallback() {              public Object doInTransaction(TransactionStatus status) {                  // 這里實(shí)現(xiàn)自己的相關(guān)業(yè)務(wù)邏輯                  updateOperation1();                  return resultOfUpdateOperation2();              }          });      }  }

在上面的例子中,我們顯示的使用了TransactionTemplate來(lái)完成事務(wù)管理,通過(guò)實(shí)現(xiàn)TransactionCallback接口并在其doInTransaction方法中完成了我們對(duì)業(yè)務(wù)的處理。我們可以大概看下TransactionTemplate的execute方法的實(shí)現(xiàn):

public <T> T execute(TransactionCallback<T> action) throws TransactionException {   Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");   if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {    return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);   }   else {             // 1.通過(guò)事務(wù)管理器開(kāi)啟事務(wù)    TransactionStatus status = this.transactionManager.getTransaction(this);    T result;    try {                 // 2.執(zhí)行傳入的業(yè)務(wù)邏輯     result = action.doInTransaction(status);    }    catch (RuntimeException | Error ex) {     // 3.出現(xiàn)異常,進(jìn)行回滾     rollbackOnException(status, ex);     throw ex;    }    catch (Throwable ex) {     // 3.出現(xiàn)異常,進(jìn)行回滾     rollbackOnException(status, ex);     throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");    }             // 4.正常執(zhí)行完成的話,提交事務(wù)    this.transactionManager.commit(status);    return result;   }  }

這些方法具體的實(shí)現(xiàn)我們暫且不看,后續(xù)進(jìn)行源碼分析時(shí)都會(huì)詳細(xì)介紹,之所以將這個(gè)代碼貼出來(lái)是讓大家更好的理解TransactionTemplate的工作機(jī)制:實(shí)際上就是通過(guò)一個(gè)TransactionCallback封裝了業(yè)務(wù)邏輯,然后TransactionTemplate會(huì)在事務(wù)的上下文中調(diào)用。

在上面的例子中doInTransaction是有返回值的,而實(shí)際上有時(shí)候并不需要返回值,這種情況下,我們可以使用TransactionCallbackWithoutResult提代TransactionCallback。

transactionTemplate.execute(new TransactionCallbackWithoutResult() {      protected void doInTransactionWithoutResult(TransactionStatus status) {          updateOperation1();          updateOperation2();      }  });

?實(shí)際上我們還可以通過(guò)TransactionTemplate指定事務(wù)的屬性,例如隔離級(jí)別、超時(shí)時(shí)間、傳播行為等等

TransactionTemplate是線程安全的,我們可以全局配置一個(gè)TransactionTemplate,然后所有的類都共享這個(gè)TransactionTemplate。但是,如果某個(gè)類需要特殊的事務(wù)配置,例如需要定制隔離級(jí)別,那么我們就有必要去創(chuàng)建不同的TransactionTemplate。?

TransactionOperator

?TransactionOperator適用于響應(yīng)式編程的情況,這里就不做詳細(xì)介紹了?

TransactionManager

實(shí)際上TransactionTemplate內(nèi)部也是使用TransactionManager來(lái)完成事務(wù)管理的,我們之前也看過(guò)它的execute方法的實(shí)現(xiàn)了,其實(shí)內(nèi)部就是調(diào)用了TransactionManager的方法,實(shí)際上就是分為這么幾步

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2.  開(kāi)啟事務(wù)

  3.  執(zhí)行業(yè)務(wù)邏輯

  4.  出現(xiàn)異常進(jìn)行回滾

  5.  正常執(zhí)行則提交事務(wù)

這里我還是直接用官網(wǎng)給出的例子

// 定義事務(wù)  DefaultTransactionDefinition def = new DefaultTransactionDefinition();  def.setName("SomeTxName");  def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);  // txManager,事務(wù)管理器  // 通過(guò)事務(wù)管理器開(kāi)啟一個(gè)事務(wù)  TransactionStatus status = txManager.getTransaction(def);  try {      // 完成自己的業(yè)務(wù)邏輯  }  catch (MyException ex) {      // 出現(xiàn)異常,進(jìn)行回滾      txManager.rollback(status);      throw ex;  }  // 正常執(zhí)行完成,提交事務(wù)  txManager.commit(status);

我們?cè)诤筮叺脑创a分析中其實(shí)重點(diǎn)分析的也就是TransactionManager的源碼。

申明式事務(wù)

在對(duì)編程式事務(wù)有一定了解之后我們會(huì)發(fā)現(xiàn),編程式事務(wù)存在下面幾個(gè)問(wèn)題:

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2.  「我們的業(yè)務(wù)代碼跟事務(wù)管理的代碼混雜在一起」。

  3.  「每個(gè)需要事務(wù)管理的地方都需要寫(xiě)重復(fù)的代碼」

如何解決呢?這就要用到申明式事務(wù)了,實(shí)現(xiàn)申明式事務(wù)一般有兩種方式

  •  基于XML配置

  •  基于注解

申明式事務(wù)事務(wù)的實(shí)現(xiàn)原理如下(圖片來(lái)源于官網(wǎng)):

總結(jié)Spring中事務(wù)的使用、抽象機(jī)制及模擬Spring事務(wù)實(shí)現(xiàn)

實(shí)現(xiàn)原理

「實(shí)際上就是結(jié)合了APO自動(dòng)代理跟事務(wù)相關(guān)API」。通過(guò)開(kāi)啟AOP自動(dòng)代理并向容器中注冊(cè)了事務(wù)需要的通知(Transaction Advisor),在Transaction Advisor調(diào)用了事務(wù)相關(guān)API,其實(shí)內(nèi)部也是調(diào)用了TransactionManager的方法。

基于XML配置這種方式就不講了,筆者近兩年時(shí)間沒(méi)用過(guò)XML配置,我們主要就看看通過(guò)注解方式來(lái)實(shí)現(xiàn)申明式事務(wù)。主要涉及到兩個(gè)核心注解

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2.  @EnableTransactionManagement

  3.  @Transactional

@EnableTransactionManagement這個(gè)注解主要有兩個(gè)作用,其一是,開(kāi)啟AOP自動(dòng)代理,其二是,添加事務(wù)需要用到的通知(Transaction Advisor),如果你對(duì)AOP有一定了解的話那你應(yīng)該知道一個(gè)Advisor實(shí)際上就是一個(gè)綁定了切點(diǎn)(Pointcut)的通知(Advice),通過(guò)@EnableTransactionManagement這個(gè)注解導(dǎo)入的Advisor所綁定的切點(diǎn)就是通過(guò)@Transactional來(lái)定義的。

申明式事務(wù)的例子我這里就省去了,我相信沒(méi)幾個(gè)人不會(huì)用吧.....

Spring對(duì)事務(wù)的抽象

Spring事務(wù)抽象的關(guān)鍵就是事務(wù)策略的概念,事務(wù)策略是通過(guò)TransactionManager接口定義的。TransactionManager本身只是一個(gè)標(biāo)記接口,它有兩個(gè)直接子接口

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2.  ReactiveTransactionManager,這個(gè)接口主要用于在響應(yīng)式編程模型下,不是我們要討論的重點(diǎn)

  3.  PlatformTransactionManager,命令式編程模型下我們使用這個(gè)接口。

?關(guān)于響應(yīng)式跟命令式編程都可以單獨(dú)寫(xiě)一篇文章了,本文重點(diǎn)不是討論這兩種編程模型,可以認(rèn)為平常我們使用的都是命令式編程?

PlatformTransactionManager

PlatformTransactionManager接口定義

public interface PlatformTransactionManager extends TransactionManager {   // 開(kāi)啟事務(wù)      TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;      // 提交事務(wù)      void commit(TransactionStatus status) throws TransactionException;      // 回滾事務(wù)      void rollback(TransactionStatus status) throws TransactionException;  }

PlatformTransactionManager是命令式編程模型下Spring事務(wù)機(jī)制的中心接口,定義了完成一個(gè)事務(wù)必須的三個(gè)步驟,也就是說(shuō)定義了事務(wù)實(shí)現(xiàn)的規(guī)范

  •  開(kāi)啟事務(wù)

  •  提交事務(wù)

  •  回滾事務(wù)

通常來(lái)說(shuō),我們不會(huì)直接實(shí)現(xiàn)這個(gè)接口,而是通過(guò)繼承AbstractPlatformTransactionManager,這個(gè)類是一個(gè)抽象類,主要用作事務(wù)管理的模板,這個(gè)抽象類已經(jīng)實(shí)現(xiàn)了事務(wù)的傳播行為以及跟事務(wù)相關(guān)的同步管理。

回頭看接口中定義的三個(gè)方法,首先是開(kāi)啟事務(wù)的方法,從方法簽名上來(lái)看,其作用就是通過(guò)一個(gè)TransactionDefinition來(lái)獲取一個(gè)TransactionStatus類型的對(duì)象。為了更好的理解Spring中事務(wù)的抽象我們有必要了解下這兩個(gè)接口

TransactionDefinition

接口定義如下:

public interface TransactionDefinition {       // 定義了7中事務(wù)的傳播機(jī)制   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;      // 4種隔離級(jí)別,-1代表的是使用數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別      // 比如在MySQL下,使用的就是ISOLATION_REPEATABLE_READ(可重復(fù)讀)   int ISOLATION_DEFAULT = -1;   int ISOLATION_READ_UNCOMMITTED = 1;     int ISOLATION_READ_COMMITTED = 2;    int ISOLATION_REPEATABLE_READ = 4;    int ISOLATION_SERIALIZABLE = 8;         // 事務(wù)的超時(shí)時(shí)間,默認(rèn)不限制時(shí)間   int TIMEOUT_DEFAULT = -1;      // 提供了對(duì)上面三個(gè)屬性的get方法   default int getPropagationBehavior() {    return PROPAGATION_REQUIRED;   }   default int getIsolationLevel() {    return ISOLATION_DEFAULT;   }   default int getTimeout() {    return TIMEOUT_DEFAULT;   }      // 事務(wù)是否是只讀的,默認(rèn)不是   default boolean isReadOnly() {    return false;   }        // 事務(wù)的名稱   @Nullable   default String getName() {    return null;   }        // 返回一個(gè)只讀的TransactionDefinition      // 只對(duì)屬性提供了getter方法,所有屬性都是接口中定義的默認(rèn)值   static TransactionDefinition withDefaults() {    return StaticTransactionDefinition.INSTANCE;   }  }

從這個(gè)接口的名字上我們也能知道,它的主要完成了對(duì)事務(wù)定義的抽象,這些定義有些是數(shù)據(jù)庫(kù)層面本身就有的,例如隔離級(jí)別、是否只讀、超時(shí)時(shí)間、名稱。也有些是Spring賦予的,例如事務(wù)的傳播機(jī)制。Spring中一共定義了7種事務(wù)的傳播機(jī)制

  •  TransactionDefinition.PROPAGATION_REQUIRED:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。

  •  TransactionDefinition.PROPAGATION_REQUIRES_NEW:創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。

  •  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ù),則拋出異常。

  •  TransactionDefinition.PROPAGATION_MANDATORY:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則拋出異常。

  •  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。

關(guān)于事務(wù)的傳播在源碼分析的文章中我會(huì)重點(diǎn)介紹,現(xiàn)在大家留個(gè)印象即可。

我們?cè)谑褂蒙昝魇绞聞?wù)的時(shí)候,會(huì)通過(guò)@Transactional這個(gè)注解去申明某個(gè)方法需要進(jìn)行事務(wù)管理,在@Transactional中可以定義事務(wù)的屬性,這些屬性實(shí)際上就會(huì)被封裝到一個(gè)TransactionDefinition中,當(dāng)然封裝的時(shí)候肯定不是直接使用的接口,而是這個(gè)接口的一個(gè)實(shí)現(xiàn)類RuleBasedTransactionAttribute。RuleBasedTransactionAttribute,該類的繼承關(guān)系如下:

總結(jié)Spring中事務(wù)的使用、抽象機(jī)制及模擬Spring事務(wù)實(shí)現(xiàn)

RuleBasedTransactionAttribute

  •  DefaultTransactionDefinition,實(shí)現(xiàn)了TransactionDefinition,并為其中的定義的屬性提供了默認(rèn)值   

// 默認(rèn)的傳播機(jī)制為required,沒(méi)有事務(wù)新建一個(gè)事務(wù)      // 有事務(wù)的話加入當(dāng)前事務(wù)      private int propagationBehavior = PROPAGATION_REQUIRED;      // 隔離級(jí)別跟數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別一直      private int isolationLevel = ISOLATION_DEFAULT;      // 默認(rèn)為-1,不設(shè)置超時(shí)時(shí)間      private int timeout = TIMEOUT_DEFAULT;      // 默認(rèn)不是只讀的      private boolean readOnly = false;
  •  TransactionAttribute,擴(kuò)展了``DefaultTransactionDefinition`,新增了兩個(gè)事務(wù)的屬性   

// 用于指定事務(wù)使用的事務(wù)管理器的名稱      String getQualifier();      // 指定在出現(xiàn)哪種異常時(shí)才進(jìn)行回滾      boolean rollbackOn(Throwable ex);
  •  DefaultTransactionAttribute,繼承了DefaultTransactionDefinition,同時(shí)實(shí)現(xiàn)了TransactionAttribute接口,定義了默認(rèn)的回滾異常 

// 拋出RuntimeException/Error才進(jìn)行回滾    public boolean rollbackOn(Throwable ex) {        return (ex instanceof RuntimeException || ex instanceof Error);    }
  •  RuleBasedTransactionAttribute,@Transactional注解的rollbackFor等屬性就會(huì)被封裝到這個(gè)類中,允許程序員自己定義回滾的異常,如果沒(méi)有指定回滾的異常,默認(rèn)「拋出RuntimeException/Error才進(jìn)行回滾」

TransactionStatus

這個(gè)接口主要用于描述Spring事務(wù)的狀態(tài),其繼承關(guān)系如下:

總結(jié)Spring中事務(wù)的使用、抽象機(jī)制及模擬Spring事務(wù)實(shí)現(xiàn)

TransactionStatus

  •  TransactionExecution,這個(gè)接口也是用于描述事務(wù)的狀態(tài),TransactionStatus是在其上做的擴(kuò)展,內(nèi)部定義了以下幾個(gè)方法 

// 判斷當(dāng)前事務(wù)是否是一個(gè)新的事務(wù)   // 不是一個(gè)新事務(wù)的話,那么需要加入到已經(jīng)存在的事務(wù)中   boolean isNewTransaction();   // 事務(wù)是否被標(biāo)記成RollbackOnly   // 如果被標(biāo)記成了RollbackOnly,意味著事務(wù)只能被回滾   void setRollbackOnly();    boolean isRollbackOnly();   // 是否事務(wù)完成,回滾或提交都意味著事務(wù)完成了   boolean isCompleted();
  •  SavepointManager,定義了管理保存點(diǎn)(Savepoint)的方法,隔離級(jí)別為NESTED時(shí)就是通過(guò)設(shè)置回滾點(diǎn)來(lái)實(shí)現(xiàn)的,內(nèi)部定義了這么幾個(gè)方法   

// 創(chuàng)建保存點(diǎn)     Object createSavepoint() throws TransactionException;     // 回滾到指定保存點(diǎn)     void rollbackToSavepoint(Object savepoint) throws TransactionException;     // 移除回滾點(diǎn)     void releaseSavepoint(Object savepoint) throws TransactionException;
  •  TransactionStatus,繼承了上面這些接口,額外提供了兩個(gè)方法   

//用于判斷當(dāng)前事務(wù)是否設(shè)置了保存點(diǎn)     boolean hasSavepoint();    // 這個(gè)方法復(fù)寫(xiě)了父接口Flushable中的方法     // 主要用于刷新會(huì)話     // 對(duì)于Hibernate/jpa而言就是調(diào)用了其session/entityManager的flush方法     void flush();

?小總結(jié):通過(guò)上面的分析我們會(huì)發(fā)現(xiàn),TransactionDefinition的主要作用是給出一份事務(wù)屬性的定義,然后事務(wù)管理器根據(jù)給出的定義來(lái)創(chuàng)建事務(wù),TransactionStatus主要是用來(lái)描述創(chuàng)建后的事務(wù)的狀態(tài)?

在對(duì)TransactionDefinition跟TransactionStatus有一定了解后,我們?cè)倩氐絇latformTransactionManager接口本身,PlatformTransactionManager作為事務(wù)管理器的基礎(chǔ)接口只是定義管理一個(gè)事務(wù)必須的三個(gè)方法:開(kāi)啟事務(wù),提交事務(wù),回滾事務(wù),接口僅僅是定義了規(guī)范而已,真正做事的還是要依賴它的實(shí)現(xiàn)類,所以我們來(lái)看看它的繼承關(guān)系

PlatformTransactionManager的實(shí)現(xiàn)類

總結(jié)Spring中事務(wù)的使用、抽象機(jī)制及模擬Spring事務(wù)實(shí)現(xiàn)

PlatformTransactionManager

  •  AbstractPlatformTransactionManager,Spring提供的一個(gè)事務(wù)管理的基類,提供了事務(wù)管理的模板,實(shí)現(xiàn)了Spring事務(wù)管理的一個(gè)標(biāo)準(zhǔn)流程

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2.  判斷當(dāng)前是否已經(jīng)存在一個(gè)事務(wù)

  3.  應(yīng)用合適的事務(wù)傳播行為

  4.  在必要的時(shí)候掛起/恢復(fù)事務(wù)

  5.  提交時(shí)檢查事務(wù)是否被標(biāo)記成為rollback-only

  6.  在回滾時(shí)做適當(dāng)?shù)男薷模ㄊ菆?zhí)行真實(shí)的回滾/還是將事務(wù)標(biāo)記成rollback-only)

觸發(fā)注冊(cè)的同步回調(diào)

在AbstractPlatformTransactionManager提供了四個(gè)常見(jiàn)的子類,其說(shuō)明如下

總結(jié)Spring中事務(wù)的使用、抽象機(jī)制及模擬Spring事務(wù)實(shí)現(xiàn)

關(guān)于事務(wù)管理器的詳細(xì)代碼分析放到下篇文章,本文對(duì)其有個(gè)大概了解即可。

Spring中事務(wù)的同步機(jī)制

Spring中事務(wù)相關(guān)的同步機(jī)制可以分為兩類

  •  資源的同步

  •  行為的同步

什么是資源的同步呢?在一個(gè)事務(wù)中我們往往會(huì)一次執(zhí)行多個(gè)SQL(如果是單條的SQL實(shí)際上沒(méi)有必要開(kāi)啟事務(wù)),為了保證事務(wù)所有的SQL都能夠使用一個(gè)數(shù)據(jù)庫(kù)連接,這個(gè)時(shí)候我們需要將數(shù)據(jù)庫(kù)連接跟事務(wù)進(jìn)行同步,這個(gè)時(shí)候數(shù)據(jù)庫(kù)連接就是跟這個(gè)事務(wù)同步的一個(gè)資源。

那什么又是行為的同步呢?還是以數(shù)據(jù)庫(kù)連接為例子,在事務(wù)開(kāi)啟之前我們需要先獲取一個(gè)數(shù)據(jù)庫(kù)連接,同樣的在事務(wù)提交時(shí)我們需要將連接關(guān)閉(不一定是真正的關(guān)閉,如果是連接池只是歸還到連接池中),這個(gè)時(shí)候關(guān)閉連接這個(gè)行為也需要跟事務(wù)進(jìn)行同步

那么Spring是如何來(lái)管理同步的呢?同樣的,Spring也提供了一個(gè)同步管理器TransactionSynchronizationManager,這是一個(gè)抽象類,其中所有的方法都是靜態(tài)的,并且所有的方法都是圍繞它所申明的幾個(gè)靜態(tài)常量字段,如下:

// 這就是同步的資源,Spring就是使用這個(gè)完成了連接的同步  private static final ThreadLocal<Map<Object, Object>> resources =      new NamedThreadLocal<>("Transactional resources");  // TransactionSynchronization完成了行為的同步  // 關(guān)于TransactionSynchronization在后文進(jìn)行分析  private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =      new NamedThreadLocal<>("Transaction synchronizations"); // 事務(wù)的名稱  private static final ThreadLocal<String> currentTransactionName =      new NamedThreadLocal<>("Current transaction name");  // 事務(wù)是否被標(biāo)記成只讀  private static final ThreadLocal<Boolean> currentTransactionReadOnly =      new NamedThreadLocal<>("Current transaction read-only status"); // 事物的隔離級(jí)別  private static final ThreadLocal<Integer> currentTransactionIsolationLevel =      new NamedThreadLocal<>("Current transaction isolation level");  // 是否真實(shí)開(kāi)啟了事務(wù)   private static final ThreadLocal<Boolean> actualTransactionActive =      new NamedThreadLocal<>("Actual transaction active");

可以看到所有的同步都是通過(guò)ThreadLocal實(shí)現(xiàn)的,對(duì)于ThreadLocal本文不做詳細(xì)分析,如果對(duì)ThreadLocal還不了解的同學(xué)也沒(méi)有關(guān)系,對(duì)于本文而言你只需要知道ThreadLocal能將資源跟當(dāng)前線程綁定即可,例如ThreadLocal<Map<Object, Object>> resources這個(gè)屬性就代表要將一個(gè)map綁定到當(dāng)前線程,它提供了set跟get方法,分別用于將屬性綁定到線程上以及獲取線程上綁定的屬性。

上面的幾個(gè)變量中除了synchronizations之外其余的應(yīng)該都很好理解,synchronizations中綁定的是一個(gè)TransactionSynchronization的集合,那么這個(gè)TransactionSynchronization有什么用呢?我們來(lái)看看它的接口定義

public interface TransactionSynchronization extends Flushable {   // 事務(wù)完成的狀態(tài)      // 0 提交      // 1 回滾      // 2 異常狀態(tài),例如在事務(wù)執(zhí)行時(shí)出現(xiàn)異常,然后回滾,回滾時(shí)又出現(xiàn)異常      // 就會(huì)被標(biāo)記成狀態(tài)2   int STATUS_COMMITTED = 0;   int STATUS_ROLLED_BACK = 1;   int STATUS_UNKNOWN = 2;      // 我們綁定的這些TransactionSynchronization需要跟事務(wù)同步      // 1.如果事務(wù)掛起,我們需要將其掛起      // 2.如果事務(wù)恢復(fù),我們需要將其恢復(fù)   default void suspend() {   }   default void resume() {   }   @Override   default void flush() {   }      // 在事務(wù)執(zhí)行過(guò)程中,提供的一些回調(diào)方法   default void beforeCommit(boolean readOnly) {   }   default void beforeCompletion() {   }   default void afterCommit() {   }   default void afterCompletion(int status) {   }  }

可以看到這個(gè)接口就是定義了一些方法,這些個(gè)方法可以在事務(wù)達(dá)到不同階段后執(zhí)行,可以認(rèn)為定義了事務(wù)執(zhí)行過(guò)程的一些回調(diào)行為,這就是我之前說(shuō)的行為的同步。

模擬Spring事務(wù)的實(shí)現(xiàn)

本文的最后一部分希望大家模擬一下Spring事務(wù)的實(shí)現(xiàn),我們利用現(xiàn)有的AOP來(lái)實(shí)現(xiàn)事務(wù)的管理。數(shù)據(jù)庫(kù)訪問(wèn)我們直接使用jdbc,在模擬之前我們先明確兩點(diǎn)

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2.  切點(diǎn)應(yīng)該如何定義?

  3.  通知要實(shí)現(xiàn)什么功能?

我們先說(shuō)第一個(gè)問(wèn)題,因?yàn)槭俏覀冏约耗M,所以關(guān)于切點(diǎn)的定義我們就設(shè)置的盡量簡(jiǎn)單一些,不妨就直接指定某個(gè)包下的所有類。對(duì)于第二個(gè)問(wèn)題,我們也不做的過(guò)于復(fù)雜,在方法執(zhí)行前開(kāi)啟事務(wù),在方法執(zhí)行后提交事務(wù)并關(guān)閉連接,所以我們需要定義一個(gè)環(huán)繞通知。同時(shí),我們也需要將連接跟事務(wù)同步,保證事務(wù)中的所有SQL共用一個(gè)事務(wù)是實(shí)現(xiàn)事務(wù)管理的必要條件?;诖?,我們開(kāi)始編寫(xiě)代碼

我們只需要引入Spring相關(guān)的依賴跟JDBC相關(guān)依賴即可,該項(xiàng)目?jī)H僅是一個(gè)Spring環(huán)境下的Java項(xiàng)目,沒(méi)有Web依賴,也不是SpringBoot項(xiàng)目,項(xiàng)目結(jié)構(gòu)如下:

POM文件:

<?xml version="1.0" encoding="UTF-8"?>  <project xmlns="http://maven.apache.org/POM/4.0.0"           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"           xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">      <modelVersion>4.0.0</modelVersion>      <groupId>com.dmz.framework</groupId>      <artifactId>mybatis</artifactId>      <version>1.0-SNAPSHOT</version>     <dependencies>              <dependency>              <groupId>mysql</groupId>              <artifactId>mysql-connector-java</artifactId>              <version>8.0.15</version>          </dependency>                <dependency>              <groupId>org.springframework</groupId>              <artifactId>spring-context</artifactId>              <version>5.2.6.RELEASE</version>          </dependency>                <dependency>              <groupId>org.springframework</groupId>              <artifactId>spring-aop</artifactId>              <version>5.2.6.RELEASE</version>          </dependency>         <dependency>              <groupId>org.aspectj</groupId>              <artifactId>aspectjweaver</artifactId>              <version>1.9.5</version>          </dependency>      </dependencies>  </project>

配置類:

// 開(kāi)啟AOP跟掃描組件即可  @EnableAspectJAutoProxy @ComponentScan("com.dmz.mybatis.tx_demo")  public class Config {  }

完成事務(wù)管理的核心類:

public class TransactionUtil {      public static final ThreadLocal<Connection> synchronousConnection =              new ThreadLocal<Connection>();      private TransactionUtil() {      }     public static Connection startTransaction() {          Connection connection = synchronousConnection.get();          if (connection == null) {              try {                  // 這里替換成你自己的連接地址即可                  connection = DriverManager                          .getConnection("jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8", "root", "123");                  synchronousConnection.set(connection);                  connection.setAutoCommit(false);              } catch (SQLException e) {                  e.printStackTrace();              }          }          return connection;      }      public static int execute(String sql, Object... args) {          Connection connection = startTransaction();          try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {              if (args != null) {                  for (int i = 1; i < args.length + 1; i++) {                      preparedStatement.setObject(i, args[i - 1]);                  }              }              return preparedStatement.executeUpdate();          } catch (SQLException e) {              e.printStackTrace();          }          return 0;      }     public static void commit() {          try (Connection connection = synchronousConnection.get()) {              connection.commit();              synchronousConnection.remove();          } catch (SQLException e) {              e.printStackTrace();          }      }      public static void rollback() {          try (Connection connection = synchronousConnection.get()) {              connection.rollback();              synchronousConnection.remove();          } catch (SQLException e) {              e.printStackTrace();          }      }  }

實(shí)際需要事務(wù)管理的類

@Component  public class UserService {      public void saveUser() {          TransactionUtil.execute                  ("INSERT INTO `test`.`user`(`id`, `name`) VALUES (?, ?)", 100, "dmz");          // 測(cè)試回滾          // throw new RuntimeException();      }  }

切面:

@Component  @Aspect  public class TxAspect {      @Pointcut("execution(public * com.dmz.mybatis.tx_demo.service..*.*(..))")      private void pointcut() {      }      @Around("pointcut()")      public Object around(JoinPoint joinPoint) throws Throwable {          // 在方法執(zhí)行前開(kāi)啟事務(wù)          TransactionUtil.startTransaction();          // 執(zhí)行業(yè)務(wù)邏輯          Object proceed = null;          try {              ProceedingJoinPoint method = (ProceedingJoinPoint) joinPoint;              proceed = method.proceed();          } catch (Throwable throwable) {              // 出現(xiàn)異常進(jìn)行回滾              TransactionUtil.rollback();              return proceed;          }          // 方法執(zhí)行完成后提交事務(wù)          TransactionUtil.commit();          return proceed;      }  }

用于測(cè)試的主函數(shù):

public class Main {      public static void main(String[] args) {          AnnotationConfigApplicationContext ac =                  new AnnotationConfigApplicationContext(Config.class);          UserService userService = ac.getBean(UserService.class);          userService.saveUser();      }  }

具體的測(cè)試過(guò)程跟測(cè)試結(jié)果我就不放了,大家把代碼拷貝過(guò)去自行測(cè)試就好了

“總結(jié)Spring中事務(wù)的使用、抽象機(jī)制及模擬Spring事務(wù)實(shí)現(xiàn)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

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

AI