spring事務(wù)管理是指在業(yè)務(wù)代碼在出現(xiàn)異常之后,對(duì)之前的操作進(jìn)行回滾,保證數(shù)據(jù)庫(kù)數(shù)據(jù)的一致性
-
分為編程式事務(wù)管理,配置式,注解式
- 編程式
TransactionDefinition def = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(def) try{ businessLogic(); transactionManager.commit(status); } catch(Exception ex) { transactionManager. rollback(status); throw ex; }
- xml配置式
<tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="create*" propagation="REQUIRED" timeout="300" rollback-for="java.lang.Exception" /> <tx:method name="delete*" propagation="REQUIRED" timeout="300" rollback-for="java.lang.Exception" /> <tx:method name="update*" propagation="REQUIRED" timeout="300" rollback-for="java.lang.Exception" /> <tx:method name="get*" propagation="REQUIRED" read-only="true" timeout="300" /> <tx:method name="*" propagation="REQUIRED" read-only="true" timeout="300" /> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="txPointcut" expression="execution(* com.mico.emptyspring.service.*ServiceA.*(..))" /> <aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice" /> </aop:config>
- 注解式,配置文件上添加配置
<tx:annotation-driven transaction-manager="txManager"/>
,接著在service類或方法上添加@Transactional注解
-
先偷個(gè)雞,事務(wù)注解可以放在控制器層嗎?
- 可以,但是在spring-framework-reference.pdf文檔中有這樣一段話:
<tx:annotation-driven/> only looks for @Transactional on beans in the same application context it is defined in. This means that, if you put <tx:annotation-driven/> in a WebApplicationContext for a DispatcherServlet, it only checks for @Transactional beans in your controllers, and not your services.這句話的意思是,<tx:annoation-driven/>只會(huì)查找和它在相同的應(yīng)用上下文文件[同一配置文件]中定義的bean上面的@Transactional注解,如果你把它放在Dispatcher的應(yīng)用上下文中,它只檢查控制器上的@Transactional注解,而不是你services上的@Transactional注解。所以org.springframework.web.servlet.DispatcherServlet
的contextConfigLocation
屬性配置的xml中配置<tx:annotation-driven/>則spring只檢查控制器上的@Transactional注解,而不是你services上的@Transactional注解。但是,不推薦這么做,嘻嘻
- 可以,但是在spring-framework-reference.pdf文檔中有這樣一段話:
@Transactional配置項(xiàng)
屬性 | 類型 | 描述 |
---|---|---|
value | String | 可選的限定描述符,指定使用的事務(wù)管理器 |
propagation | enum: Propagation | 可選的事務(wù)傳播行為設(shè)置 |
isolation | enum: Isolation | 可選的事務(wù)隔離級(jí)別設(shè)置 |
readOnly | boolean | 讀寫或只讀事務(wù),默認(rèn)讀寫 |
timeout | int (in seconds granularity) | 事務(wù)超時(shí)時(shí)間設(shè)置 |
rollbackFor | Class對(duì)象數(shù)組,必須繼承自Throwable | 導(dǎo)致事務(wù)回滾的異常類數(shù)組 |
rollbackForClassName | 類名數(shù)組,必須繼承自Throwable | 導(dǎo)致事務(wù)回滾的異常類名字?jǐn)?shù)組 |
noRollbackFor | Class對(duì)象數(shù)組,必須繼承自Throwable | 不會(huì)導(dǎo)致事務(wù)回滾的異常類數(shù)組 |
noRollbackForClassName | 類名數(shù)組,必須繼承自Throwable | 不會(huì)導(dǎo)致事務(wù)回滾的異常類名字?jǐn)?shù)組 |
value主要用來指定不同的事務(wù)管理器;主要用來滿足在同一個(gè)系統(tǒng)中,存在不同的事務(wù)管理器。比如在Spring中,聲明了兩種事務(wù)管理器txManager1, txManager2.然后,用戶可以根據(jù)這個(gè)參數(shù)來根據(jù)需要指定特定的txManager.在一個(gè)系統(tǒng)中,需要訪問多個(gè)數(shù)據(jù)源或者多個(gè)數(shù)據(jù)庫(kù),則必然會(huì)配置多個(gè)事務(wù)管理器的
-
臟讀,不可重復(fù)讀和幻象讀
- Dirty reads--讀臟數(shù)據(jù)。也就是說,比如事務(wù)A的未提交(還依然緩存)的數(shù)據(jù)被事務(wù)B讀走,如果事務(wù)A失敗回滾,會(huì)導(dǎo)致事務(wù)B所讀取的的數(shù)據(jù)是錯(cuò)誤的。
- non-repeatable reads--數(shù)據(jù)不可重復(fù)讀。比如事務(wù)A中兩處讀取數(shù)據(jù)-total-的值。在第一讀的時(shí)候,total是100,然后事務(wù)B就把total的數(shù)據(jù)改成 200,事務(wù)A再讀一次,結(jié)果就發(fā)現(xiàn),total竟然就變成200了,造成事務(wù)A數(shù)據(jù)混亂。
- phantom reads--幻象讀數(shù)據(jù),這個(gè)和non-repeatable reads相似,也是同一個(gè)事務(wù)中多次讀不一致的問題。但是non-repeatable reads的不一致是因?yàn)樗〉臄?shù)據(jù)集被改變了(比如total的數(shù)據(jù)),但是phantom reads所要讀的數(shù)據(jù)的不一致卻不是他所要讀的數(shù)據(jù)集改變,而是他的條件數(shù)據(jù)集改變。比如Select account.id where account.name="ppgogo*",第一次讀去了6個(gè)符合條件的id,第二次讀取的時(shí)候,由于事務(wù)b把一個(gè)帳號(hào)的名字由"dd"改成"ppgogo1",結(jié)果取出來了7個(gè)數(shù)據(jù)。
-
事務(wù)的隔離級(jí)別:是指若干個(gè)并發(fā)的事務(wù)之間的隔離程度
- @Transactional(isolation = Isolation.READ_UNCOMMITTED):讀取未提交數(shù)據(jù)(會(huì)出現(xiàn)臟讀, 不可重復(fù)讀) 基本不使用。
- @Transactional(isolation = Isolation.READ_COMMITTED):讀取已提交數(shù)據(jù)(會(huì)出現(xiàn)不可重復(fù)讀和幻讀)大多數(shù)主流數(shù)據(jù)庫(kù)的默認(rèn)事務(wù)等級(jí),保證了一個(gè)事務(wù)不會(huì)讀到另一個(gè)并行事務(wù)已修改但未提交的數(shù)據(jù),避免了“臟讀取”。該級(jí)別適用于大多數(shù)系統(tǒng)。
- @Transactional(isolation = Isolation.REPEATABLE_READ):可重復(fù)讀(會(huì)出現(xiàn)幻讀),保證了一個(gè)事務(wù)不會(huì)修改已經(jīng)由另一個(gè)事務(wù)讀取但未提交(回滾)的數(shù)據(jù)。避免了“臟讀取”和“不可重復(fù)讀取”的情況,但是帶來了更多的性能損失。
- @Transactional(isolation = Isolation.SERIALIZABLE):最嚴(yán)格的級(jí)別,事務(wù)串行執(zhí)行,資源消耗最大;
事務(wù)隔離級(jí)別和臟讀,幻象讀,不可重復(fù)讀
Isolation | Dirty reads | Non-repeatable reads | Phantom reads |
---|---|---|---|
Serializable | 不會(huì) | 不會(huì) | 不會(huì) |
REPEATABLE READ | 不會(huì) | 不會(huì) | 會(huì) |
READ COMMITTED | 不會(huì) | 會(huì) | 會(huì) |
Read Uncommitted | 會(huì) | 會(huì) | 會(huì) |
-
事務(wù)傳播屬性:如果在開始當(dāng)前事務(wù)之前,一個(gè)事務(wù)上下文已經(jīng)存在,此時(shí)有若干選項(xiàng)可以指定一個(gè)事務(wù)性方法的執(zhí)行行為,即事務(wù)方法的嵌套調(diào)用會(huì)產(chǎn)生事務(wù)傳播
- TransactionDefinition.PROPAGATION_REQUIRED:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。這是默認(rèn)值。
- TransactionDefinition.PROPAGATION_REQUIRES_NEW:創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。
- 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ù),則拋出異常。
- TransactionDefinition.PROPAGATION_MANDATORY:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則拋出異常。
- TransactionDefinition.PROPAGATION_NESTED:如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運(yùn)行;如果當(dāng)前沒有事務(wù),則該取值等價(jià)于TransactionDefinition.PROPAGATION_REQUIRED。
- REQUIRED_NEW和NESTED兩種不同的傳播機(jī)制的區(qū)別:
- REQUIRED_NEW:內(nèi)部的事務(wù)獨(dú)立運(yùn)行,在各自的作用域中,可以獨(dú)立的回滾或者提交;而外部的事務(wù)將不受內(nèi)部事務(wù)的回滾狀態(tài)影響.啟動(dòng)一個(gè)新的, 不依賴于環(huán)境的 “內(nèi)部” 事務(wù). 這個(gè)事務(wù)將被完全 commited 或 rolled back 而不依賴于外部事務(wù), 它擁有自己的隔離范圍, 自己的鎖, 等等. 當(dāng)內(nèi)部事務(wù)開始執(zhí)行時(shí), 外部事務(wù)將被掛起, 內(nèi)務(wù)事務(wù)結(jié)束時(shí), 外部事務(wù)將繼續(xù)執(zhí)行.
- NESTED:的事務(wù),基于單一的事務(wù)來管理,提供了多個(gè)保存點(diǎn)。這種多個(gè)保存點(diǎn)的機(jī)制允許內(nèi)部事務(wù)的變更觸發(fā)外部事務(wù)的回滾。如果外部事務(wù) commit, 嵌套事務(wù)也會(huì)被 commit;如果外部事務(wù) roll back, 嵌套事務(wù)也會(huì)被 roll back 。開始一個(gè) “嵌套的” 事務(wù), 它是已經(jīng)存在事務(wù)的一個(gè)真正的子事務(wù). 嵌套事務(wù)開始執(zhí)行時(shí), 它將取得一個(gè) savepoint. 如果這個(gè)嵌套事務(wù)失敗, 我們將回滾到此 savepoint. 嵌套事務(wù)是外部事務(wù)的一部分, 只有外部事務(wù)結(jié)束后它才會(huì)被提交
-
@Transactional 注意事項(xiàng)
- 用在接口實(shí)現(xiàn)類或接口實(shí)現(xiàn)方法上,而不是接口類中,Spring 建議不要在接口或者接口方法上使用@Transactional注解,因?yàn)檫@只有在使用基于接口的代理時(shí)它才會(huì)生效
- @Transactional 注解應(yīng)該只被應(yīng)用到 public 方法上,
這是由 Spring AOP 的本質(zhì)決定的。如果你在 protected、private 或者默認(rèn)可見性的方法上使用 @Transactional 注解,這將被忽略,也不會(huì)拋出任何異常。 - 將@Transactional放置在需要進(jìn)行事務(wù)管理的方法上,而不是不假思索的放置在接口實(shí)現(xiàn)類上( 接口中所有方法都需要進(jìn)行事務(wù)管理,但其實(shí)并不需要,如只讀的接口就不需要事務(wù)管理,但是 由于配置了@Transactional就需要AOP攔截及事務(wù)的處理,影響系統(tǒng)性能)
- 方法上注解屬性會(huì)覆蓋類注解上的相同屬性,當(dāng)接口與接口中方法上同時(shí)帶有@Transactional注解時(shí),方法上的事務(wù)注解會(huì)覆蓋類上的注解
- 用 spring 事務(wù)管理器,由spring來負(fù)責(zé)數(shù)據(jù)庫(kù)的打開,提交,回滾.默認(rèn)遇到運(yùn)行期例外(throw new RuntimeException(“注釋”);)會(huì)回滾,即遇到不受檢查(unchecked)的例外時(shí)回滾;而遇到需要捕獲的例外(throw new Exception(“注釋”);)不會(huì)回滾,即遇到受檢查的例外(就是非運(yùn)行時(shí)拋出的異常,編譯器會(huì)檢查到的異常叫受檢查例外或說受檢查異常)時(shí),需我們指定方式來讓事務(wù)回滾要想所有異常都回滾,要加上 @Transactional( rollbackFor={Exception.class,其它異常}) .如果讓unchecked例外不回滾:@Transactional(notRollbackFor=RunTimeException.class)
- 默認(rèn)情況下,只有來自外部的方法調(diào)用才會(huì)被AOP代理捕獲,也就是,類內(nèi)部方法調(diào)用本類內(nèi)部的其他方法并不會(huì)引起事務(wù)行為,即使被調(diào)用方法使用@Transactional注解進(jìn)行修飾,如下
- 前置條件:同一個(gè)類中方法,A方法無(wú)@Transactional 注解,B使用了
- 同一個(gè)類中A 調(diào)用 B ;則外部調(diào)用A之后,B的事務(wù)是不會(huì)起作用的
- 若是有上層(按照 Controller層、Service層、DAO層的順序)由Controller 調(diào)用 Service 直接調(diào)用B方法,發(fā)生異常會(huì)發(fā)生回滾;若間接調(diào)用,Action 調(diào)用 Service 中 的A 方法,A無(wú)@Transactional 注解,B有,A調(diào)用B,B的注解無(wú)效
- 前置條件:同一個(gè)類中方法,A方法無(wú)@Transactional 注解,B使用了
- 父類的聲明的@Transactional會(huì)對(duì)子類的所有方法進(jìn)行事務(wù)增強(qiáng);子類覆蓋重寫父類方式可覆蓋其@Transactional中的聲明配置
-
多線程下事務(wù)管理
- 因?yàn)榫€程不屬于spring托管,故線程不能夠默認(rèn)使用spring的事務(wù),也不能獲取spring注入的bean
- 在被spring聲明式事務(wù)管理的方法內(nèi)開啟多線程,多線程內(nèi)的方法不被事務(wù)控制
- 單線程的情況下,一個(gè)事務(wù)會(huì)在層級(jí)式調(diào)用的Spring組件之間傳播。但是在@Transactional注解的服務(wù)方法會(huì)產(chǎn)生一個(gè)新的線程的情況下,事務(wù)是不會(huì)從調(diào)用者線程傳播到新建線程的。結(jié)果會(huì)是被調(diào)用者線程會(huì)說缺少事務(wù),