javascript
Spring 事务原理和使用
轉(zhuǎn)載自??Spring 事務(wù)原理和使用
1.Spring@Transactional的配置
步驟一、在Spring配置文件中引入命名空間
<beans?xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans?http://www.springframework.org/schema/beans/spring-beans-2.0.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.0.xsd">步驟二、xml配置文件中,添加事務(wù)管理器bean配置
<!--?事務(wù)管理器配置,單數(shù)據(jù)源事務(wù)?--><bean?id="pkgouTransactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property?name="dataSource"?ref="pkGouDataSource"?/></bean> <!--?使用annotation定義事務(wù)?--><tx:annotation-driven?transaction-manager="pkgouTransactionManager"?/>步驟三、在使用事務(wù)的方法或者類上添加下面的注解
@Transactional(“pkgouTransactionManager”)?
2.傳播行為和隔離級(jí)別
1> 事務(wù)注解方式: @Transactional
-
標(biāo)注在類前:標(biāo)示類中所有方法都進(jìn)行事務(wù)處理
-
標(biāo)注在接口、實(shí)現(xiàn)類的方法前:標(biāo)示方法進(jìn)行事務(wù)處理
2> 事務(wù)傳播行為介紹:
?
3> 事務(wù)超時(shí)設(shè)置:
@Transactional(timeout=30)?//默認(rèn)是30秒4> 事務(wù)隔離級(jí)別:
?
-
臟讀 : 一個(gè)事務(wù)讀取到另一事務(wù)未提交的更新數(shù)據(jù)
-
不可重復(fù)讀 : 在同一事務(wù)中, 多次讀取同一數(shù)據(jù)返回的結(jié)果有所不同, 換句話說(shuō), 后續(xù)讀取可以讀到另一事務(wù)已提交的更新數(shù)據(jù)。相反,”可重復(fù)讀”在同一事務(wù)中多次讀取數(shù)據(jù)時(shí),能夠保證所讀數(shù)據(jù)一樣,也就是后續(xù)讀取不能讀到另一事務(wù)已提交的更新數(shù)據(jù)
-
幻讀 : 一個(gè)事務(wù)讀到另一個(gè)事務(wù)已提交的insert數(shù)據(jù)
@Transactional的屬性:
?
3.工作原理
自動(dòng)提交
默認(rèn)情況下,數(shù)據(jù)庫(kù)處于自動(dòng)提交模式。每一條語(yǔ)句處于一個(gè)單獨(dú)的事務(wù)中,在這條語(yǔ)句執(zhí)行完畢時(shí),如果執(zhí)行成功則隱式的提交事務(wù),如果執(zhí)行失敗則隱式的回滾事務(wù)。 事務(wù)管理,是一組相關(guān)的操作處于一個(gè)事務(wù)之中,因此必須關(guān)閉數(shù)據(jù)庫(kù)的自動(dòng)提交模式。這點(diǎn),Spring會(huì)在org/springframework/jdbc/datasource/DataSourceTransactionManager.java中將底層連接的自動(dòng)提交特性設(shè)置為false。
//?switch?to?manual?commit?if?necessary。?this?is?very?expensive?in?some?jdbc?drivers, //?so?we?don't?want?to?do?it?unnecessarily?(for?example?if?we've?explicitly //?configured?the?connection?pool?to?set?it?already)。if?(con。getautocommit())? {txobject.setmustrestoreautocommit(true);if?(logger.isdebugenabled())?{logger.debug("switching?jdbc?connection?["?+?con?+?"]?to?manual?commit");}//首先將自動(dòng)提交屬性改為falsecon.setautocommit(false); }spring事務(wù)回滾規(guī)則
Spring事務(wù)管理器回滾一個(gè)事務(wù)的推薦方法是在當(dāng)前事務(wù)的上下文內(nèi)拋出異常。Spring事務(wù)管理器會(huì)捕捉任何未處理的異常,然后依據(jù)規(guī)則決定是否回滾拋出異常的事務(wù)。 默認(rèn)配置下,Spring只有在拋出的異常為運(yùn)行時(shí)unchecked異常時(shí)才回滾該事務(wù),也就是拋出的異常為RuntimeException的子類(Errors也會(huì)導(dǎo)致事務(wù)回滾)。而拋出checked異常則不會(huì)導(dǎo)致事務(wù)回滾。 Spring也支持明確的配置在拋出哪些異常時(shí)回滾事務(wù),包括checked異常。也可以明確定義哪些異常拋出時(shí)不回滾事務(wù)。 還可以編程性的通過(guò)setRollbackOnly()方法來(lái)指示一個(gè)事務(wù)必須回滾,在調(diào)用完setRollbackOnly()后你所能執(zhí)行的唯一操作就是回滾。
4.注意事項(xiàng)
由于Spring事務(wù)管理是基于接口代理或動(dòng)態(tài)字節(jié)碼技術(shù),通過(guò)AOP實(shí)施事務(wù)增強(qiáng)的。
(1)對(duì)于基于接口動(dòng)態(tài)代理的AOP事務(wù)增強(qiáng)來(lái)說(shuō),由于接口的方法是public的,這就要求實(shí)現(xiàn)類的實(shí)現(xiàn)方法必須是public的(不能是protected,private等),同時(shí)不能使用static的修飾符。所以,可以實(shí)施接口動(dòng)態(tài)代理的方法只能是使用“public” 或 “public final”修飾符的方法,其它方法不可能被動(dòng)態(tài)代理,相應(yīng)的也就不能實(shí)施AOP增強(qiáng),也即不能進(jìn)行Spring事務(wù)增強(qiáng)。
(2)基于CGLib字節(jié)碼動(dòng)態(tài)代理的方案是通過(guò)擴(kuò)展被增強(qiáng)類,動(dòng)態(tài)創(chuàng)建子類的方式進(jìn)行AOP增強(qiáng)植入的。由于使用final,static,private修飾符的方法都不能被子類覆蓋,相應(yīng)的,這些方法將不能被實(shí)施的AOP增強(qiáng)。
所以,必須特別注意這些修飾符的使用,@Transactional 注解只被應(yīng)用到 public 可見(jiàn)度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不會(huì)報(bào)錯(cuò),但是這個(gè)被注解的方法將不會(huì)展示已配置的事務(wù)設(shè)置。
用 spring 事務(wù)管理器,由spring來(lái)負(fù)責(zé)數(shù)據(jù)庫(kù)的打開(kāi),提交,回滾。默認(rèn)遇到運(yùn)行期異常(throw new RuntimeException(“注釋”);)會(huì)回滾,即遇到不受檢查(unchecked)的異常時(shí)回滾;
@Transactional(rollbackFor=Exception.class)?//指定回滾,遇到異常Exception時(shí)回滾 public?void?methodName()? { throw?new?Exception("注釋"); }而遇到需要捕獲的異常(throw new Exception(“注釋”);)不會(huì)回滾,即遇到受檢查的異常(就是非運(yùn)行時(shí)拋出的異常,編譯器會(huì)檢查到的異常叫受檢查異常或說(shuō)受檢查異常)時(shí),需我們指定方式來(lái)讓事務(wù)回滾 要想所有異常都回滾,要加上 @Transactional(rollbackFor={Exception。class,其它異常}) 。如果讓unchecked異常不回滾:
@Transactional(notRollbackFor=RunTimeException.class)如下:@Transactional(noRollbackFor=Exception.class)//指定不回滾,遇到運(yùn)行期異常(throw?new?RuntimeException("注釋");)會(huì)回滾 public?ItimDaoImpl?getItemDaoImpl()? { throw?new?RuntimeException("注釋"); }僅僅 @Transactional注解的出現(xiàn)不足于開(kāi)啟事務(wù)行為,它僅僅是一種元數(shù)據(jù),能夠被可以識(shí)別 @Transactional注解和上述的配置適當(dāng)?shù)木哂惺聞?wù)行為的beans所使用。其實(shí),根本上是 元素的出現(xiàn) 開(kāi)啟了事務(wù)行為。
Spring團(tuán)隊(duì)的建議是你在具體的類(或類的方法)上使用 @Transactional 注解,而不要使用在類所要實(shí)現(xiàn)的任何接口上。你當(dāng)然可以在接口上使用 @Transactional 注解,但是這將只能當(dāng)你設(shè)置了基于接口的代理時(shí)它才生效。因?yàn)樽⒔馐遣荒芾^承的,這就意味著如果你正在使用基于類的代理時(shí),那么事務(wù)的設(shè)置將不能被基于類的代理所識(shí)別,而且對(duì)象也將不會(huì)被事務(wù)代理所包裝(將被確認(rèn)為嚴(yán)重的)。因此,請(qǐng)接受Spring團(tuán)隊(duì)的建議并且在具體的類火方法上使用 @Transactional 注解。
@Transactional 注解標(biāo)識(shí)的方法,處理過(guò)程盡量的簡(jiǎn)單。尤其是帶鎖的事務(wù)方法,能不放在事務(wù)里面的最好不要放在事務(wù)里面。可以將常規(guī)的數(shù)據(jù)庫(kù)查詢操作放在事務(wù)前面進(jìn)行,而事務(wù)內(nèi)進(jìn)行增、刪、改、加鎖查詢等操作。
@Transactional 注解的默認(rèn)事務(wù)管理器bean是“transactionManager”,如果聲明為其他名稱的事務(wù)管理器,需要在方法上添加@Transational(“managerName”)。
@Transactional 注解標(biāo)注的方法中不要出現(xiàn)網(wǎng)絡(luò)調(diào)用、比較耗時(shí)的處理程序,因?yàn)?#xff0c;事務(wù)中數(shù)據(jù)庫(kù)連接是不會(huì)釋放的,如果每個(gè)事務(wù)的處理時(shí)間都非常長(zhǎng),那么寶貴的數(shù)據(jù)庫(kù)連接資源將很快被耗盡。
5.自我調(diào)用中的問(wèn)題?
Spring事務(wù)使用AOP代理后的方法調(diào)用執(zhí)行流程,如圖所示:
?
從圖中可以看出,調(diào)用事務(wù)時(shí)首先調(diào)用的是AOP代理對(duì)象而不是目標(biāo)對(duì)象,首先執(zhí)行事務(wù)切面,事務(wù)切面內(nèi)部通過(guò)TransactionInterceptor環(huán)繞增強(qiáng)進(jìn)行事務(wù)的增強(qiáng)。即進(jìn)入目標(biāo)方法之前開(kāi)啟事務(wù),退出目標(biāo)方法時(shí)提交/回滾事務(wù)。
這樣在自我調(diào)用時(shí),則會(huì)出現(xiàn)無(wú)法開(kāi)啟事務(wù)的問(wèn)題,比如:
public?interface?TargetService? {??public?void?a();??public?void?b();?? }?? @Service? public?class?TargetServiceImpl?implements?TargetService {??public?void?a()?{??this.b();??}??@Transactional(propagation?=?Propagation.REQUIRES_NEW)??public?void?b()?{//執(zhí)行數(shù)據(jù)庫(kù)操作}?? }此處的this指向目標(biāo)對(duì)象,因此調(diào)用this.b()將不會(huì)執(zhí)行b事務(wù)切面,即不會(huì)執(zhí)行事務(wù)增強(qiáng),因此b方法的事務(wù)定義“@Transactional(propagation = Propagation.REQUIRES_NEW)”將不會(huì)實(shí)施,即結(jié)果是b和a方法的事務(wù)是方法的事務(wù)定義是一樣的。
解決方法 通過(guò)BeanPostProcessor 在目標(biāo)對(duì)象中注入代理對(duì)象:
一、定義BeanPostProcessor 需要使用的標(biāo)識(shí)接口
public?interface?BeanSelfAware {public?abstract?void?setSelf(Object?obj); }二、定義自己的BeanPostProcessor(InjectBeanSelfProcessor)
public?class?InjectBeanSelfProcessorimplements?BeanPostProcessor,?ApplicationContextAware {ApplicationContext?context;private?static?Log?log?=?LogFactory.getLog(com/netease/lottery/base/common/BeanSelf/InjectBeanSelfProcessor); public?InjectBeanSelfProcessor() { } public?void?setApplicationContext(ApplicationContext?context)throws?BeansException {this.context?=?context; } public?Object?postProcessAfterInitialization(Object?bean,?String?beanName)throws?BeansException {if(bean?instanceof?BeanSelfAware){//如果Bean實(shí)現(xiàn)了BeanSelfAware標(biāo)識(shí)接口,就將代理對(duì)象注入BeanSelfAware?myBean?=?(BeanSelfAware)bean;Class?cls?=?bean.getClass();if(!AopUtils.isAopProxy(bean)){Class?c?=?bean.getClass();Service?serviceAnnotation?=?(Service)c.getAnnotation(org/springframework/stereotype/Service);if(serviceAnnotation?!=?null)try{bean?=?context.getBean(beanName);if(AopUtils.isAopProxy(bean));}catch(BeanCurrentlyInCreationException?beancurrentlyincreationexception)?{?}catch(Exception?ex){log.fatal((new?StringBuilder()).append("No?Proxy?Bean?for?service?").append(bean.getClass()).append("?").append(ex.getMessage()).toString(),?ex);}}myBean.setSelf(bean);return?myBean;}?else{return?bean;} } public?Object?postProcessBeforeInitialization(Object?bean,?String?beanName)throws?BeansException {return?bean; }三、目標(biāo)類實(shí)現(xiàn)
public?interface?TargetService? {?? public?void?a();?? public?void?b();?? }?? @Service? public?class?TargetServiceImpl?implements?TargetService,BeanSelfAware {?? private?TargetService?self;?? public?void?setSelf(Object?proxyBean)? {?//通過(guò)InjectBeanSelfProcessor注入自己(目標(biāo)對(duì)象)的AOP代理對(duì)象??this.self?=?(TargetService)?proxyBean;?? }?? public?void?a()? {??self.b();?? }?? @Transactional(propagation?=?Propagation.REQUIRES_NEW)?? public?void?b()? { //執(zhí)行數(shù)據(jù)庫(kù)操作 }?? }postProcessAfterInitialization根據(jù)目標(biāo)對(duì)象是否實(shí)現(xiàn)BeanSelfAware標(biāo)識(shí)接口,通過(guò)setSelf(bean)將代理對(duì)象(bean)注入到目標(biāo)對(duì)象中,從而可以完成目標(biāo)對(duì)象內(nèi)部的自我調(diào)用。
總結(jié)
以上是生活随笔為你收集整理的Spring 事务原理和使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 炫舞对电脑配置要求(炫舞对电脑配置)
- 下一篇: gradle idea java ssm