日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

五分鐘教你手寫 SpringBoot 本地事務管理實現

瀏覽:27日期:2023-03-24 11:29:05

白菜Java自習室 涵蓋核心知識

1. SpringBoot 事務

一直在用 SpringBoot 中的 @Transactional 來做事務管理,但是很少沒想過 SpringBoot 是如何實現事務管理的,今天從源碼入手,看看 @Transactional 是如何實現事務的,最后我們結合源碼的理解,自己動手寫一個類似的注解來實現事務管理,幫助我們加深理解。

1.1. 事務的隔離級別

事務為什么需要隔離級別呢?這是因為在并發事務情況下,如果沒有隔離級別會導致如下問題:

臟讀 (Dirty Read) :當A事務對數據進行修改,但是這種修改還沒有提交到數據庫中,B事務同時在訪問這個數據,由于沒有隔離,B獲取的數據有可能被A事務回滾,這就導致了數據不一致的問題。 丟失修改 (Lost To Modify):當A事務訪問數據100,并且修改為100-1=99,同時B事務讀取數據也是100,修改數據100-1=99,最終兩個事務的修改結果為99,但是實際是98。事務A修改的數據被丟失了。 不可重復讀 (Unrepeatable Read):指A事務在讀取數據X=100的時候,B事務把數據X=100修改為X=200,這個時候A事務第二次讀取數據X的時候,發現X=200了,導致了在整個A事務期間,兩次讀取數據X不一致了,這就是不可重復讀。 幻讀 (Phantom Read):幻讀和不可重復讀類似。幻讀表現在,當A事務讀取表數據時候,只有3條數據,這個時候B事務插入了2條數據,當A事務再次讀取的時候,發現有5條記錄了,平白無故多了2條記錄,就像幻覺一樣。

不可重復讀 VS 幻讀

不可重復讀的重點是修改 :同樣的條件 , 你讀取過的數據 , 再次讀取出來發現值不一樣了,重點在更新操作。幻讀的重點在于新增或者刪除:同樣的條件 , 第 1 次和第 2 次讀出來的記錄數不一樣,重點在增刪操作。

所以,為了避免上述的問題,事務中就有了隔離級別的概念,在Spring中定義了五種表示隔離級別的常量 TransactionDefinition:

ISOLATION_DEFAULT:數據庫默認的隔離級別,MySQL默認采用的 REPEATABLE_READ 隔離級別。 ISOLATION_READ_UNCOMMITTED:最低的隔離級別,允許讀取未提交的數據變更,可能會導致臟讀、幻讀或不可重復讀。 ISOLATION_READ_COMMITTED:允許讀取并發事務已經提交的數據,可以阻止臟讀,但是幻讀或不可重復讀仍有可能發生。 ISOLATION_REPEATABLE_READ:對同一字段的多次讀取結果都是一致的,除非數據是被本身事務自己所修改,可以阻止臟讀和不可重復讀,但幻讀仍有可能發生。MySQL中通過MVCC解決了該隔離級別下出現幻讀的可能。 ISOLATION_SERIALIZABLE:串行化隔離級別,該級別可以防止臟讀、不可重復讀以及幻讀,但是串行化會影響性能。1.2. Spring中事務的傳播機制

為什么Spring中要搞一套事務的傳播機制呢?這是Spring給我們提供的事務增強工具,主要是解決方法之間調用,事務如何處理的問題。比如有方法A、方法B和方法C,在A中調用了方法B和方法C。偽代碼如下:

MethodA() { MethodB(); MethodC();}

假設三個方法中都開啟了自己的事務,那么他們之間是什么關系呢?MethodA的回滾會影響MethodB和MethodC嗎?Spring中的事務傳播機制就是解決這個問題的。Spring中定義了七種事務傳播行為:

PROPAGATION_REQUIRED: 如果存在一個事務,則支持當前事務。如果沒有事務則開啟一個新的事務。 PROPAGATION_SUPPORTS: 如果存在一個事務,支持當前事務。如果沒有事務,則非事務的執行。但是對于事務同步的事務管理器,PROPAGATION_SUPPORTS與不使用事務有少許不同。 PROPAGATION_MANDATORY: 如果已經存在一個事務,支持當前事務。如果沒有一個活動的事務,則拋出異常。 PROPAGATION_REQUIRES_NEW: 總是開啟一個新的事務。如果一個事務已經存在,則將這個存在的事務掛起。 PROPAGATION_NOT_SUPPORTED: 總是非事務地執行,并掛起任何存在的事務。 PROPAGATION_NEVER: 總是非事務地執行,如果存在一個活動事務,則拋出異常。 PROPAGATION_NESTED: 如果一個活動的事務存在,則運行在一個嵌套的事務中。 如果沒有活動事務, 則按 TransactionDefinition.PROPAGATION_REQUIRED 屬性執行。1.3. Spring中事務如何實現異常回滾的

回顧完了事務的相關知識,接下來我們正式來研究下 Spring Boot 中如何通過 @Transactional 來管理事務的,我們重點看看它是如何實現回滾的。在 Spring 中 TransactionInterceptor 和 PlatformTransactionManager 這兩個類是整個事務模塊的核心,我們重點研究下這兩個類的源碼。

TransactionInterceptor 負責攔截方法執行,進行判斷是否需要提交或者回滾事務。 PlatformTransactionManager 是 Spring 中的事務管理接口,真正定義了事務如何回滾和提交。

TransactionInterceptor 類中的代碼有很多,我簡化一下邏輯,方便說明:

// 以下代碼省略部分內容 public Object invoke(MethodInvocation invocation) throws Throwable { // 獲取事務調用的目標方法 Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); // 執行帶事務調用 return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed); }

invokeWithinTransaction 簡化邏輯如下:

// 以下代碼省略部分內容 protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable { Object retVal; try { // 調用真正的方法體 retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // 如果出現異常,執行事務異常處理 completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { // 最后做一下清理工作,主要是緩存和狀態等 cleanupTransactionInfo(txInfo); } // 如果沒有異常,直接提交事務 commitTransactionAfterReturning(txInfo); return retVal; }

事務出現異常回滾的邏輯 completeTransactionAfterThrowing 如下:

// 以下代碼省略部分內容 protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) { // 判斷是否需要回滾,判斷的邏輯就是看有沒有聲明事務屬性,同時判斷是不是在目前的這個異常中執行回滾 if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) { // 執行回滾 txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); } else { // 否則不需要回滾,直接提交即可 txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } }

上面的代碼已經把 Spring 的事務的基本原理說清楚了,如何進行判斷執行事務,如何回滾。下面到了真正執行回滾邏輯的代碼中 PlatformTransactionManager 接口的子類,我們以 JDBC 的事務為例,DataSourceTransactionManager 就是 jdbc 的事務管理類。跟蹤上面的代碼rollback(txInfo.getTransactionStatus()) 可以發現最終執行的代碼如下:

@Override protected void doRollback(DefaultTransactionStatus status) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction(); Connection con = txObject.getConnectionHolder().getConnection(); if (status.isDebug()) { logger.debug('Rolling back JDBC transaction on Connection [' + con + ']'); } try { // 調用jdbc的 rollback進行回滾事務 con.rollback(); } catch (SQLException ex) { throw new TransactionSystemException('Could not roll back JDBC transaction', ex); } }

這里小結下 Spring 中事務的實現思路,Spring 主要依靠 TransactionInterceptor 來攔截執行方法體,判斷是否開啟事務,然后執行事務方法體,方法體中 catch 住異常,接著判斷是否需要回滾,如果需要回滾就委托真正的 TransactionManager 比如 JDBC 中的 DataSourceTransactionManager 來執行回滾邏輯。提交事務也是同樣的道理。這里用個流程圖展示下思路:

五分鐘教你手寫 SpringBoot 本地事務管理實現

2. 手寫注解實現事務回滾

我們弄清楚了 Spring 的事務執行流程,那我們可以模仿著自己寫一個注解,實現遇到指定異常就回滾的功能。這里持久層就以最簡單的 JDBC 為例。我們先梳理下需求,首先注解我們可以基于 Spring 的 AOP 來實現,接著既然是 JDBC,那么我們需要一個類來幫我們管理連接,用來判斷異常是否回滾或者提交。

2.1. Maven 加入依賴

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>2.2. 新建一個注解

@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic @interface MyTransaction { // 指定異常回滾 Class<? extends Throwable>[] rollbackFor() default {};}2.3. 新建連接管理器

該類幫助我們管理連接,該類的核心功能是把取出的連接對象綁定到線程上,方便在 AOP 處理中取出,進行提交或者回滾操作。

@Componentpublic class DataSourceConnectHolder { @Autowired private DataSource dataSource; /** * 線程綁定對象 */ ThreadLocal<Connection> resources = new NamedThreadLocal<>('Transactional resources'); public Connection getConnection() { Connection con = resources.get(); if (con != null) { return con; } try { con = dataSource.getConnection(); // 為了體現事務,全部設置為手動提交事務 con.setAutoCommit(false); } catch (SQLException e) { e.printStackTrace(); } resources.set(con); return con; } public void cleanHolder() { Connection con = resources.get(); if (con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } resources.remove(); }}2.4. 新建一個切面

這部分是事務處理的核心,先獲取注解上的異常類,然后捕獲住執行的異常,判斷異常是不是注解上的異常或者其子類,如果是就回滾,否則就提交。

@Aspect@Componentpublic class MyTransactionAopHandler { @Autowired private DataSourceConnectHolder connectHolder; Class<? extends Throwable>[] es; // 攔截所有MyTransaction注解的方法 @org.aspectj.lang.annotation.Pointcut('@annotation(你的包路徑.MyTransaction)') public void Transaction() { } @Around('Transaction()') public Object TransactionProceed(ProceedingJoinPoint proceed) throws Throwable { Object result = null; Signature signature = proceed.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); if (method == null) { return result; } MyTransaction transaction = method.getAnnotation(MyTransaction.class); if (transaction != null) { es = transaction.rollbackFor(); } try { result = proceed.proceed(); } catch (Throwable throwable) { // 異常處理 completeTransactionAfterThrowing(throwable); throw throwable; } // 直接提交 doCommit(); return result; } /** * 執行回滾,最后關閉連接和清理線程綁定 */ private void doRollBack() { try { connectHolder.getConnection().rollback(); } catch (SQLException e) { e.printStackTrace(); } finally { connectHolder.cleanHolder(); } } /** * 執行提交,最后關閉連接和清理線程綁定 */ private void doCommit() { try { connectHolder.getConnection().commit(); } catch (SQLException e) { e.printStackTrace(); } finally { connectHolder.cleanHolder(); } } /** * 異常處理,捕獲的異常是目標異常或者其子類,就進行回滾,否則就提交事務。 */ private void completeTransactionAfterThrowing(Throwable throwable) { if (es != null && es.length > 0) { for (Class<? extends Throwable> e : es) { if (e.isAssignableFrom(throwable.getClass())) { doRollBack(); } } } doCommit(); }}2.4. 編寫一個 Service

saveTest 方法調用了2個插入語句,同時聲明了 @MyTransaction 事務注解,遇到 Exception 就進行回滾。

@Servicepublic class MyTransactionTest { @Autowired private DataSourceConnectHolder holder; // 一個事務中執行兩個sql插入 @MyTransaction(rollbackFor = NullPointerException.class) public void saveTest(int id) { save(id, '白菜Java自習室'); save(id + 10, '白菜Java自習室'); throw new RuntimeException(); } // 執行sql private void save(int id, String value) { String sql = 'insert into test values(?,?)'; Connection connection = holder.getConnection(); PreparedStatement stmt = null; try { stmt = connection.prepareStatement(sql); stmt.setInt(1, id); stmt.setString(2, value); stmt.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } }}

我們自己通過 JDBC 結合 Spring 的 AOP 自己寫了個 @MyTransactional 的注解,實現了遇到指定異常回滾的功能。

到此這篇關于五分鐘教你手寫 SpringBoot 本地事務管理實現的文章就介紹到這了,更多相關SpringBoot 本地事務管理內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Spring
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
亚洲精品国模| 亚洲中午字幕| 欧美aⅴ一区二区三区视频| 欧美伊人影院| 国产精品手机在线播放| 国产精品视频一区二区三区四蜜臂 | 国产精品jk白丝蜜臀av小说| 青青伊人久久| 国产精品视频一区二区三区综合| 麻豆精品新av中文字幕| 丰满少妇一区| 欧洲一区二区三区精品| 国产字幕视频一区二区| 婷婷中文字幕一区| 免费看日韩精品| 青青草91视频| 老司机免费视频一区二区| 四虎成人av| 欧美影院三区| 中文字幕一区二区三区四区久久| 日韩精品免费视频人成| 欧美激情视频一区二区三区在线播放| 成人三级高清视频在线看| 国精品一区二区三区| 一二三区精品| 久久精品国产999大香线蕉| 色88888久久久久久影院| 美女久久一区| 国产精品日韩精品中文字幕| 国产精品字幕| 亚洲人成高清| 久久超级碰碰| 亚洲二区免费| 日韩高清一级| 日韩福利一区| 偷拍亚洲精品| 国产高潮在线| 亚洲三区欧美一区国产二区| 久久99高清| 影音先锋久久| 精品国产精品久久一区免费式| se01亚洲视频| 偷拍亚洲精品| 免费一二一二在线视频| 亚洲毛片在线| 午夜精品成人av| 一区二区电影| 国产精品久久久久久久免费观看 | 日韩在线不卡| 婷婷成人av| 吉吉日韩欧美| 亚洲人成亚洲精品| 亚洲精品成人图区| 日韩在线成人| 亚洲一级影院| 另类欧美日韩国产在线| 午夜一区在线| 给我免费播放日韩视频| 一区二区三区午夜视频| 在线天堂中文资源最新版| 亚洲综合中文| 久久在线电影| 久久超级碰碰| 在线观看一区| 999国产精品视频| 久久99久久久精品欧美| 免费在线成人网| 亚洲精品一区三区三区在线观看| 国产美女亚洲精品7777| 亚洲尤物在线| 99tv成人| 成人国产精品一区二区免费麻豆| 日韩精品一二三区| 99久久99视频只有精品| 麻豆国产91在线播放| 日本在线成人| 夜夜嗨一区二区| 午夜精品成人av| 精品国产中文字幕第一页| 婷婷五月色综合香五月| 中文字幕在线视频网站| 一区二区亚洲视频| 欧美精品资源| 国产精品麻豆成人av电影艾秋 | 少妇精品导航| 国产日韩一区二区三免费高清| 国产精品日本| 久久国产精品成人免费观看的软件| 久久一区亚洲| 国产欧美一区二区精品久久久| 亚洲一区网站| 欧美精品羞羞答答| 欧美日韩尤物久久| 91嫩草亚洲精品| 国产精品久久久久久久久久妞妞| 日韩综合小视频| 久久亚洲不卡| 黄色成人在线网址| 亚洲性色av| 神马久久午夜| 国产伊人久久| 精品一区二区男人吃奶 | 国产videos久久| 欧美激情精品| 国产精品一区免费在线| 国产欧美日韩视频在线| 日韩激情精品| 欧美日韩a区| 亚洲91在线| 亚洲精品免费观看| 亚洲日产国产精品| 美女久久一区| 免费视频久久| 视频在线观看91| 一区二区三区四区在线观看国产日韩| 午夜视频精品| 影音先锋国产精品| 日韩视频中文| 国产农村妇女精品一二区| 99精品99| 亚洲免费高清| 亚洲欧美日韩在线观看a三区| 黄色亚洲免费| 麻豆成人在线| 亚洲三区欧美一区国产二区| 免费不卡在线视频| 天堂成人免费av电影一区| 先锋亚洲精品| 在线精品亚洲| 日韩精品一区二区三区av| 日本不卡一二三区黄网| 久久国产精品色av免费看| 欧美一级网站| 麻豆国产精品一区二区三区| 国产成人精品一区二区三区免费| 国产精品二区不卡| 久久在线视频免费观看| 欧美成人国产| 香蕉久久久久久久av网站| 午夜视频一区二区在线观看| 国产日韩欧美一区二区三区在线观看| 国产精品久久久久毛片大屁完整版 | 欧美日韩99| 老司机精品视频在线播放| 97精品一区| 精品一区免费| 日本中文字幕一区二区视频| 国产精品对白| 亚洲天堂一区二区| 噜噜噜躁狠狠躁狠狠精品视频| 日韩综合小视频| 国产一区丝袜| 午夜久久福利| 欧美视频一区| 麻豆国产精品| 欧美美女一区| 欧美日韩网址| 日韩久久一区二区三区| 国产视频一区免费看| 日韩av资源网| 亚洲永久av| 自拍自偷一区二区三区| 精品美女视频 | 91精品尤物| 免费一二一二在线视频| 热久久国产精品| 精品国产一区二区三区性色av| 日韩电影免费在线观看| 亚洲一级淫片| 97精品国产| 中文一区一区三区免费在线观 | 国产成人久久精品麻豆二区| 在线成人直播| 国产精品巨作av| 亚洲精品97| 国产精品一区二区免费福利视频 | 久久久久91| 欧美一区自拍| 欧美jjzz| 88久久精品| 美女亚洲一区| 久久av偷拍| 日本欧美在线看| 日韩欧美一区二区三区免费看| 亚洲精品大全| 999久久久精品国产| 国产探花一区在线观看| 欧美a级一区| 六月丁香综合在线视频| 亚洲欧美久久久| 国产精品福利在线观看播放| 视频一区欧美精品| 正在播放日韩精品| 日本欧美一区二区在线观看| 国产综合色区在线观看| 国产精品久久久久久妇女| 亚洲一区欧美激情| 亚洲不卡系列| 久久精品国产免费| 日韩精品第一|