<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
記錄總結Spring核心知識點:事務使用與它的傳播機制
這裡不打算討論Spring底層原始碼,只討論測試場景和總結. 不斷整理讓大腦中的知識體系沉澱。
某專案系統中,serviceA 中呼叫的 serviceB ,並且對 serviceB 進行 tryCache
@Service("testAService") public class TestAServiceImpl implements TestAService { @Resource private TestAMapper testAMapper; @Resource private TestBService testBService; @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void saveTestA(TestA entity) { testAMapper.insertSelective(entity); try { testBService.saveTestB(new TestB()); } catch (Exception e) { logger.error("呼叫B失敗", e); } // 模擬做其他的資料庫操作事情 testAMapper.updateSelective(entity); } }
testBService 中模擬丟擲異常:
@Service("testBService") public class TestBServiceImpl mplements TestBService { @Resource private TestBMapper testBMapper; @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) @Override public void saveTestB(TestB entity) { testBMapper.insertSelective(entity); throw new RuntimeException("自定義異常"); } }
問 在Controller層中呼叫 TestAService.saveTestA() 會怎麼樣?
@Resource private TestAService testAService; @ApiOperation(value = "Spring事務巢狀測試") @GetMapping("springTransactionTest") public ResponseVO<NoBody> springTransactionTest() { testAService.saveTestA(new TestA()); return ResponseVO.success(); }
答案是:
testAService testBService 中的資料庫操作 全部回滾,並且丟擲的錯誤異常:
Transaction rolled back because it has been marked as rollback-only
原因是:
testBService.saveTestB 也增加了同樣的事務註解 @Transactional
且事務隔離機制為 “REQUIRED” ,因此 兩方法執行期間為同一個資料庫事務,被同一個Spring事務管理器所管理著。
由於最開始開啟事務者為 testAService.saveTestA,則真正執行回滾操作在 saveTestA 方法中 (Spring 規定了只有新建立的事務才會真正進行提交或回滾),
因此 saveTestB 方法中異常時只設定了當前事務狀態為 RollbackOnly
org.springframework.jdbc.datasource.DataSourceTransactionManager#doSetRollbackOnly
雖然 saveTestA 中 tryCache 了 saveTestB 中的異常,企圖吃掉異常資訊讓 saveTestA 中的事務正常提交,但是 saveTestB 裡面已經設定了 當前事務狀態為 RollbackOnly, 出現了衝突矛盾!
因此事務全部回滾,並且丟擲異常資訊:
Transaction rolled back because it has been marked as rollback-only
首先,事務一般是關係型資料庫中的概念,主要目的就是 保證一系列的增刪改 SQL操作 要麼全部成功,要麼全部回滾。
在MySQL中採用SQL命令進行事務管理:
這裡重點說下 多條SQL在一個事務中,其中有部分SQL執行失敗情況下,最終執行結果是什麼
> begin;
> insert_sql1 (insert into test1(id,name)value(1,'aaa')) ;
> insert_sql2 (insert into test2(id,name)value(1,'bbb')) ;
> commit/ rollback;
上面示意程式碼,如果 insert_sql1 成功, insert_sql2 失敗時,請問 insert_sql1 最終是否插入成功?
答案是:
首先事務不會馬上回滾, 其次如果 此時執行commit
則 insert_sql1 會插入成功 ,如果執行rollback
則insert_sql1 會回滾。
那一般事務什麼時候自動回滾或者自動提交?這裡記錄一下常見場景:
JDBC中連線資料庫進行事務管理:
JDBC 事務管理的本質還是連線了資料庫執行各類資料庫中開啟關閉事務的SQL命令
Spring通過自身AOP切面功能,代理各個業務方法呼叫 JDBC中的方法進行開啟、關閉、提交、回滾事務等操作。
至於巢狀事務、各類傳播機制是如何實現, 這裡簡單總結,雖然不能體現Spring 事務操作方面的強大,但可以很快有個大致理解。
Spring 通過 一個Map
存放了當前資料庫連線物件,這是為了解決根據設定的傳播機制 ( propagation ) 決定是否要新開一個事務,新開另外一個事務需要重新申請一個資料庫連線。
Spring 通過 資料庫事務中的 SAVEPOINT
保留點功能實現 巢狀事務的傳播機制。
這裡記錄一下一個HTTP請求 從Controller層發起資料庫操作請求到回滾的log紀錄檔,用於加強理解:
@ApiOperation(value = "事務回滾測試") @PostMapping("rollbackTest") public ResponseVO<NoBody> rollbackTest() { // 簡單模擬插入一條記錄 testAService.saveTestA(new TestA()); return ResponseVO.success(); }
[http-nio-9902-exec-5] o.s.web.servlet.DispatcherServlet : POST "/api/rollbackTest", parameters={}
[http-nio-9902-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.guzt.main.model.test.web.DbTestController#rollbackTest()
[http-nio-9902-exec-5] o.s.j.d.DataSourceTransactionManager : Creating new transaction with name [com.sdjictec.wms.main.model.test.service.impl.TestAServiceImpl.saveTestA]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception
[http-nio-9902-exec-5] o.s.j.d.DataSourceTransactionManager : Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@7fec11ca] for JDBC transaction
[http-nio-9902-exec-5] o.s.j.d.DataSourceTransactionManager : Switching JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@7fec11ca] to manual commit
[http-nio-9902-exec-5] c.s.w.m.m.t.d.T.insertSelective : ==> Preparing: INSERT INTO t_test_a ( ID,NAME ) VALUES(?,? )
[http-nio-9902-exec-5] c.s.w.m.m.t.d.T.insertSelective : ==> Parameters: 1655001437939(String), p4xfy8(String)
[http-nio-9902-exec-5] c.s.w.m.m.t.d.T.insertSelective : <== Updates: 1
[http-nio-9902-exec-5] o.s.j.d.DataSourceTransactionManager : Initiating transaction rollback
[http-nio-9902-exec-5] o.s.j.d.DataSourceTransactionManager : Rolling back JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@7fec11ca]
[http-nio-9902-exec-5] o.s.j.d.DataSourceTransactionManager : Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@7fec11ca] after transaction
[http-nio-9902-exec-5] c.s.w.m.c.p.context.CurrentUserContext : CurrentUserContext remove CurrentUserVO...
[http-nio-9902-exec-5] c.s.w.m.f.a.CurrentUserContextAspect : CurrentUserContextAspect doAfterThrowing CurrentUserContext.remove()...
[http-nio-9902-exec-5] .m.m.a.ExceptionHandlerExceptionResolver : Using @ExceptionHandler com.guzt.main.framework.exception.GlobalExceptionHandler#handleBusinessException(BusinessException)
[http-nio-9902-exec-5] c.s.w.m.f.e.GlobalExceptionHandler : BusinessException -- errorCode:E5111 errorMsg:模擬資料庫操作異常,主鍵重複
[http-nio-9902-exec-5] m.m.a.RequestResponseBodyMethodProcessor : Using 'application/json', given [*/*] and supported [application/json, application/json, application/*+json, application/json, application/*+json, application/cbor]
[http-nio-9902-exec-5] m.m.a.RequestResponseBodyMethodProcessor : Writing [ResponseVO(code=-1, message=模擬資料庫操作異常,主鍵重複, data={"errorBody":"","bussinessCode":"E5111","extraMsg":""})]
[http-nio-9902-exec-5] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [com.guzt.starter.common.exception.BusinessException: errorCode=E5111, errorMsg=FAIL]
[http-nio-9902-exec-5] o.s.web.servlet.DispatcherServlet : Completed 200 OK
主要的步驟:
Creating new transaction
DataSourceTransactionManager 根據 TransactionDefinition 建立 TransactionStatus 物件,準備開啟事務Acquired Connection for JDBC transaction
通過資料庫連線池 申領一個資料庫連線Switching JDBC Connection to manual commit
開啟事務,底層是通過JDBC執行 SET autocommit = 0;
Initiating transaction rollback
準備回滾事務,修改 TransactionStatus 狀態為回滾Rolling back JDBC transaction
回滾事務 ,底層是通過JDBC執行 rollback; SET autocommit = 1;
Releasing JDBC Connection
釋放資料庫連線,最後是關閉了資料庫連線,底層呼叫了 JDBC Connection.close()
上面紀錄檔中提到了 TransactionDefinition TransactionStatus 等介面,這裡也順便總結一下Spring事務中重要的幾個介面。
介面 | 含義 | 說明 |
---|---|---|
PlatformTransactionManager | 事務管理器 | 各類資料庫操作框架自行實現該介面,例如 DataSourceTransactionManager(JDBC), JtaTransactionManager, HibernateTransactionManager |
TransactionDefinition | 事務定義資訊(隔離級別、傳播行為、超時、唯讀、回滾規則) , 一般在@Transactional 中指定這些屬性 | 如果是註解方式的事務,Spring 通過 AnnotationTransactionAttributeSource.getTransactionAttribute(Method method, Class<?> targetClass) 建立,引數中的method就是開啟事務的業務方法 |
TransactionStatus | 事務執行狀態,包含是否已完成,是否唯讀,是否有恢復點,是否只回滾等 | 事務管理器介面 PlatformTransactionManager 通過 getTransaction(TransactionDefinition definition) 方法來得到一個事務 |
至於具體功能流程,這裡不討論,自行看原始碼,多debug即可明白。
本次討論重點就是 Spring的事務管理中,遇到了程式異常到底會不會回滾?
執行事務的方法如果感知到了異常則將回滾事務
一般方法上增加了 @Transactional 註解或 其他Spring事務支援的方式AOP代理了的方法
@Transactional public void saveTestA(TestA entity) { 業務程式碼 }
被Spring事務AOP所代理的業務方法執行時出現異常,且異常類在Spring事務回滾範圍內的將被呼叫方法所感知。
預設Spring只對 unchecked Exception 進行回滾,一般手動設定全部異常(rollbackFor = Exception.class)
一般這是討論事務不生效的場景。
@Transactional public void saveTestA(TestA entity) { try{ 業務程式碼 } catch (Exception e) { logger.error("內部消化異常,不往外拋", e); } }
註解所在的類沒有被Spring 事務AOP代理
這種場景問題最隱蔽,一般需要有經驗或者多次debug才能發現
同一個類裡面方法互相呼叫,一般建議採用 SpringUtil.getBean(this.getClass()).xxxx事務方法()
某些策略模式場景,需要將service物件放到一個 Map<String, Service>中 ,如果是自行放置,則物件必須是 代理物件而非 this物件,建議採用 SpringUtil.getBean(this.getClass()) 物件。
參考程式碼:
public class WxinOrderTypeServiceImpl implements OrderTypeService { @PostConstruct public void init() { ORDER_TYPE_SERVICE.put("orderTypeKey", SpringUtil.getBean(this.getClass()); } @Transactional(rollbackFor = Exception.class) @Override public void saveOrder(Order order) logger.info("微信訂單"); } }
上面程式碼意思是,將Spring代理的物件 WxinOrderTypeServiceImpl$Cjlibxxxx 放置到策略map ORDER_TYPE_SERVICE中去,如果用 this ( ORDER_TYPE_SERVICE.put("orderTypeKey",this)
) 則事務不生效,因為this雖然也在Spring beanFactory中但沒有被事務AOP所代理,因此用this 會不生效。
備註: SpringUtil 其實就是實現了介面 ApplicationContextAware
獲得ApplicationContext,ApplicationContext 中有 getBean方法,當然SpringBoot 可以在業務類裡面直接注入 ApplicationContext
@Autowired ApplicationContext applicationContext;
資料庫本身不支援事務
如果使用MySQL且儲存引擎是MyISAM,則事務是不起作用的
異常被感知後,Spring將會做回滾或更新TransactionStatus的狀態(doSetRollbackOnly).
一旦TransactionStatus 被打上了 RollbackOnly標誌後,那麼不管中間的業務程式碼是什麼 都會丟擲異常進行全部事務回滾。
那麼什麼時候 不做回滾只更新 TransactionStatus 為 RollbackOnly?
參見文章一開始的業務程式碼,同屬於一個事務中(Propagation.REQUIRED)的多個執行事務方法(業務程式碼中巢狀呼叫其他service方法),如果不是首次開啟事務的那個方法則都只會更新 TransactionStatus 為 RollbackOnly,事務的提交回滾由首次事務開啟的那個方法執行
參見原始碼 org.springframework.transaction.support.AbstractPlatformTransactionManager# processRollback
事務回滾到哪一個程度,是全部巢狀呼叫的方法都回滾還是部分方法回滾,這裡主要是由Spring的事務傳播機制功能控制。
分類 | 行為 | 說明 | 回滾程度 |
---|---|---|---|
加入當前事務 | PROPAGATION_REQUIRED | 預設方式,如果當前存在事務,則加入該事務;如果當前沒有事務,則建立一個新的事務 | 全部回滾 |
加入當前事務 | PROPAGATION_SUPPORTS | 如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續執行 | 全部回滾 |
加入當前事務 | PROPAGATION_MANDATORY | 如果當前存在事務,則加入該事務;如果當前沒有事務,則丟擲異常 | 全部回滾 |
不加入當前事務 | PROPAGATION_REQUIRES_NEW | 建立一個新的事務,則把當前事務掛起 | Java裡面還是同一個執行緒,只新建立了另外一個資料庫連線開啟事務,如果新事務回滾且程式異常被當前事務方法感知,則當前事務方法也同樣回滾 |
不加入當前事務 | PROPAGATION_NOT_SUPPORTED | 以非事務方式執行,如果當前存在事務,則把當前事務掛起 | Java裡面還是同一個執行緒 如果程式異常被當前事務方法感知,則當前事務方法也同樣回滾 |
不加入當前事務 | PROPAGATION_NEVER | 以非事務方式執行,如果當前存在事務,則丟擲異常 | 丟擲異常,全部回滾 |
巢狀當前事務 | PROPAGATION_NESTED | 如果當前存在事務,則建立一個事務巢狀在當前事務中執行;如果當前沒有事務,則該取值等價於 PROPAGATION_REQUIRED | 只回滾自己 底層採用資料庫SavePoint功能 |
到此這篇關於Spring事務管理中的異常回滾是什麼的文章就介紹到這了,更多相關Spring異常回滾內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45