原文出處: 張開濤
數(shù)據(jù)庫事務概述
事務首先是一系列操作組成的工作單元荞胡,該工作單元內(nèi)的操作是不可分割的茫经,即要么所有操作都做,要么所有操作都不做瓷炮,這就是事務葱色。
事務必需滿足ACID(原子性、一致性娘香、隔離性和持久性)特性苍狰,缺一不可:
- 原子性(Atomicity):即事務是不可分割的最小工作單元,事務內(nèi)的操作要么全做茅主,要么全不做舞痰;
- 一致性(Consistency):在事務執(zhí)行前數(shù)據(jù)庫的數(shù)據(jù)處于正確的狀態(tài)土榴,而事務執(zhí)行完成后數(shù)據(jù)庫的數(shù)據(jù)還是處于正確的狀態(tài)诀姚,即數(shù)據(jù)完整性約束沒有被破壞;如銀行轉(zhuǎn)帳玷禽,A轉(zhuǎn)帳給B赫段,必須保證A的錢一定轉(zhuǎn)給B,一定不會出現(xiàn)A的錢轉(zhuǎn)了但B沒收到矢赁,否則數(shù)據(jù)庫的數(shù)據(jù)就處于不一致(不正確)的狀態(tài)糯笙。
- 隔離性(Isolation):并發(fā)事務執(zhí)行之間無影響,在一個事務內(nèi)部的操作對其他事務是不產(chǎn)生影響撩银,這需要事務隔離級別來指定隔離性给涕;
- 持久性(Durability):事務一旦執(zhí)行成功,它對數(shù)據(jù)庫的數(shù)據(jù)的改變必須是永久的额获,不會因比如遇到系統(tǒng)故障或斷電造成數(shù)據(jù)不一致或丟失够庙。
在實際項目開發(fā)中數(shù)據(jù)庫操作一般都是并發(fā)執(zhí)行的,即有多個事務并發(fā)執(zhí)行抄邀,并發(fā)執(zhí)行就可能遇到問題耘眨,目前常見的問題如下:
- 丟失更新:兩個事務同時更新一行數(shù)據(jù),最后一個事務的更新會覆蓋掉第一個事務的更新境肾,從而導致第一個事務更新的數(shù)據(jù)丟失剔难,這是由于沒有加鎖造成的;
- 臟讀:一個事務看到了另一個事務未提交的更新數(shù)據(jù)奥喻;
- 不可重復讀:在同一事務中偶宫,多次讀取同一數(shù)據(jù)卻返回不同的結(jié)果;也就是有其他事務更改了這些數(shù)據(jù)环鲤;
- 幻讀:一個事務在執(zhí)行過程中讀取到了另一個事務已提交的插入數(shù)據(jù)读宙;即在第一個事務開始時讀取到一批數(shù)據(jù),但此后另一個事務又插入了新數(shù)據(jù)并提交楔绞,此時第一個事務又讀取這批數(shù)據(jù)但發(fā)現(xiàn)多了一條结闸,即好像發(fā)生幻覺一樣唇兑。
為了解決這些并發(fā)問題,需要通過數(shù)據(jù)庫隔離級別來解決桦锄,在標準SQL規(guī)范中定義了四種隔離級別:
- 未提交讀(Read Uncommitted):最低隔離級別扎附,一個事務能讀取到別的事務未提交的更新數(shù)據(jù),很不安全结耀,可能出現(xiàn)丟失更新留夜、臟讀、不可重復讀图甜、幻讀碍粥;
- 提交讀(Read Committed):一個事務能讀取到別的事務提交的更新數(shù)據(jù),不能看到未提交的更新數(shù)據(jù)黑毅,不可能可能出現(xiàn)丟失更新嚼摩、臟讀,但可能出現(xiàn)不可重復讀矿瘦、幻讀枕面;
- 可重復讀(Repeatable Read):保證同一事務中先后執(zhí)行的多次查詢將返回同一結(jié)果缚去,不受其他事務影響潮秘,可能可能出現(xiàn)丟失更新、臟讀易结、不可重復讀枕荞,但可能出現(xiàn)幻讀;
- 序列化(Serializable):最高隔離級別搞动,不允許事務并發(fā)執(zhí)行躏精,而必須串行化執(zhí)行,最安全滋尉,不可能出現(xiàn)更新玉控、臟讀、不可重復讀狮惜、幻讀高诺。
隔離級別越高,數(shù)據(jù)庫事務并發(fā)執(zhí)行性能越差碾篡,能處理的操作越少虱而。因此在實際項目開發(fā)中為了考慮并發(fā)性能一般使用提交讀隔離級別,它能避免丟失更新和臟讀开泽,盡管不可重復讀和幻讀不能避免牡拇,但可以在可能出現(xiàn)的場合使用悲觀鎖或樂觀鎖來解決這些問題。
事務類型
數(shù)據(jù)庫事務類型有本地事務和分布式事務:
- 本地事務:就是普通事務,能保證單臺數(shù)據(jù)庫上的操作的ACID惠呼,被限定在一臺數(shù)據(jù)庫上导俘;
- 分布式事務:涉及兩個或多個數(shù)據(jù)庫源的事務,即跨越多臺同類或異類數(shù)據(jù)庫的事務(由每臺數(shù)據(jù)庫的本地事務組成的)剔蹋,分布式事務旨在保證這些本地事務的所有操作的ACID旅薄,使事務可以跨越多臺數(shù)據(jù)庫;
Java事務類型有JDBC事務和JTA事務:
- JDBC事務:就是數(shù)據(jù)庫事務類型中的本地事務泣崩,通過Connection對象的控制來管理事務少梁;
- JTA事務:JTA指Java事務API(Java Transaction API),是Java EE數(shù)據(jù)庫事務規(guī)范矫付, JTA只提供了事務管理接口凯沪,由應用程序服務器廠商(如WebSphere Application Server)提供實現(xiàn),JTA事務比JDBC更強大买优,支持分布式事務妨马。
Java EE事務類型有本地事務和全局事務:
- 本地事務:使用JDBC編程實現(xiàn)事務;
- 全局事務:由應用程序服務器提供而叼,使用JTA事務身笤;
按是否通過編程實現(xiàn)事務有聲明式事務和編程式事務豹悬;
- 聲明式事務: 通過注解或XML配置文件指定事務信息葵陵;
- 編程式事務:通過編寫代碼實現(xiàn)事務。
Spring提供的事務管理
Spring框架最核心功能之一就是事務管理瞻佛,而且提供一致的事務管理抽象脱篙,這能幫助我們:
- 提供一致的編程式事務管理API,不管使用Spring JDBC框架還是集成第三方框架使用該API進行事務編程伤柄;
- 無侵入式的聲明式事務支持绊困。
Spring支持聲明式事務和編程式事務事務類型。
spring事務特性
spring所有的事務管理策略類都繼承自org.springframework.transaction.PlatformTransactionManager接口
其中TransactionDefinition接口定義以下特性:
事務隔離級別
隔離級別是指若干個并發(fā)的事務之間的隔離程度适刀。TransactionDefinition 接口中定義了五個表示隔離級別的常量:
- TransactionDefinition.ISOLATION_DEFAULT:這是默認值秤朗,表示使用底層數(shù)據(jù)庫的默認隔離級別。對大部分數(shù)據(jù)庫而言笔喉,通常這值就是TransactionDefinition.ISOLATION_READ_COMMITTED取视。
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED:該隔離級別表示一個事務可以讀取另一個事務修改但還沒有提交的數(shù)據(jù)。該級別不能防止臟讀常挚,不可重復讀和幻讀作谭,因此很少使用該隔離級別。比如PostgreSQL實際上并沒有此級別奄毡。
- TransactionDefinition.ISOLATION_READ_COMMITTED:該隔離級別表示一個事務只能讀取另一個事務已經(jīng)提交的數(shù)據(jù)折欠。該級別可以防止臟讀,這也是大多數(shù)情況下的推薦值。
- TransactionDefinition.ISOLATION_REPEATABLE_READ:該隔離級別表示一個事務在整個過程中可以多次重復執(zhí)行某個查詢锐秦,并且每次返回的記錄都相同咪奖。該級別可以防止臟讀和不可重復讀。
- TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事務依次逐個執(zhí)行酱床,這樣事務之間就完全不可能產(chǎn)生干擾赡艰,也就是說,該級別可以防止臟讀斤葱、不可重復讀以及幻讀慷垮。但是這將嚴重影響程序的性能。通常情況下也不會用到該級別揍堕。
事務傳播行為
所謂事務的傳播行為是指料身,如果在開始當前事務之前,一個事務上下文已經(jīng)存在衩茸,此時有若干選項可以指定一個事務性方法的執(zhí)行行為芹血。在TransactionDefinition定義中包括了如下幾個表示傳播行為的常量:
- TransactionDefinition.PROPAGATION_REQUIRED:如果當前存在事務,則加入該事務楞慈;如果當前沒有事務幔烛,則創(chuàng)建一個新的事務。這是默認值囊蓝。
- TransactionDefinition.PROPAGATION_REQUIRES_NEW:創(chuàng)建一個新的事務饿悬,如果當前存在事務,則把當前事務掛起聚霜。
- TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務狡恬,則加入該事務;如果當前沒有事務蝎宇,則以非事務的方式繼續(xù)運行弟劲。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,如果當前存在事務姥芥,則把當前事務掛起兔乞。
- TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,如果當前存在事務凉唐,則拋出異常庸追。
- TransactionDefinition.PROPAGATION_MANDATORY:如果當前存在事務,則加入該事務熊榛;如果當前沒有事務锚国,則拋出異常。
- TransactionDefinition.PROPAGATION_NESTED:如果當前存在事務玄坦,則創(chuàng)建一個事務作為當前事務的嵌套事務來運行血筑;如果當前沒有事務绘沉,則該取值等價于TransactionDefinition.PROPAGATION_REQUIRED。
事務超時
所謂事務超時豺总,就是指一個事務所允許執(zhí)行的最長時間车伞,如果超過該時間限制但事務還沒有完成,則自動回滾事務喻喳。在 TransactionDefinition 中以 int 的值來表示超時時間另玖,其單位是秒。
默認設置為底層事務系統(tǒng)的超時值表伦,如果底層數(shù)據(jù)庫事務系統(tǒng)沒有設置超時值谦去,那么就是none,沒有超時限制蹦哼。
事務只讀屬性
只讀事務用于客戶代碼只讀但不修改數(shù)據(jù)的情形鳄哭,只讀事務用于特定情景下的優(yōu)化,比如使用Hibernate的時候纲熏。
默認為讀寫事務妆丘。
概述
Spring框架支持事務管理的核心是事務管理器抽象,對于不同的數(shù)據(jù)訪問框架(如Hibernate)通過實現(xiàn)策略接口PlatformTransactionManager局劲,從而能支持各種數(shù)據(jù)訪問框架的事務管理勺拣,PlatformTransactionManager接口定義如下:
java代碼:
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
- getTransaction():返回一個已經(jīng)激活的事務或創(chuàng)建一個新的事務(根據(jù)給定的TransactionDefinition類型參數(shù)定義的事務屬性),返回的是TransactionStatus對象代表了當前事務的狀態(tài)鱼填,其中該方法拋出TransactionException(未檢查異常)表示事務由于某種原因失敗药有。
- commit():用于提交TransactionStatus參數(shù)代表的事務,具體語義請參考Spring Javadoc剔氏;
- rollback():用于回滾TransactionStatus參數(shù)代表的事務塑猖,具體語義請參考Spring Javadoc竹祷。
TransactionDefinition接口定義如下:
java代碼:
public interface TransactionDefinition {
int getPropagationBehavior();
int getIsolationLevel();
int getTimeout();
boolean isReadOnly();
String getName();
}
- getPropagationBehavior():返回定義的事務傳播行為谈跛;
- getIsolationLevel():返回定義的事務隔離級別;
- getTimeout():返回定義的事務超時時間塑陵;
- isReadOnly():返回定義的事務是否是只讀的感憾;
- getName():返回定義的事務名字。
TransactionStatus接口定義如下:
java代碼:
public interface TransactionStatus extends SavepointManager {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
}
- isNewTransaction():返回當前事務狀態(tài)是否是新事務令花;
- hasSavepoint():返回當前事務是否有保存點阻桅;
- setRollbackOnly():設置當前事務應該回滾;
- isRollbackOnly(():返回當前事務是否應該回滾兼都;
- flush():用于刷新底層會話中的修改到數(shù)據(jù)庫嫂沉,一般用于刷新如Hibernate/JPA的會話,可能對如JDBC類型的事務無任何影響扮碧;
- isCompleted():當前事務否已經(jīng)完成趟章。
內(nèi)置事務管理器實現(xiàn)
Spring提供了許多內(nèi)置事務管理器實現(xiàn):
- DataSourceTransactionManager:位于org.springframework.jdbc.datasource包中杏糙,數(shù)據(jù)源事務管理器,提供對單個javax.sql.DataSource事務管理蚓土,用于Spring JDBC抽象框架宏侍、iBATIS或MyBatis框架的事務管理;
- JdoTransactionManager:位于org.springframework.orm.jdo包中蜀漆,提供對單個javax.jdo.PersistenceManagerFactory事務管理谅河,用于集成JDO框架時的事務管理;
- JpaTransactionManager:位于org.springframework.orm.jpa包中确丢,提供對單個javax.persistence.EntityManagerFactory事務支持绷耍,用于集成JPA實現(xiàn)框架時的事務管理;
- HibernateTransactionManager:位于org.springframework.orm.hibernate3包中鲜侥,提供對單個org.hibernate.SessionFactory事務支持锨天,用于集成Hibernate框架時的事務管理;該事務管理器只支持Hibernate3+版本剃毒,且Spring3.0+版本只支持Hibernate 3.2+版本病袄;
- JtaTransactionManager:位于org.springframework.transaction.jta包中,提供對分布式事務管理的支持赘阀,并將事務管理委托給Java EE應用服務器事務管理器益缠;
- OC4JjtaTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的對OC4J10.1.3+應用服務器事務管理器的適配器基公,此適配器用于對應用服務器提供的高級事務的支持幅慌;
- WebSphereUowTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的對WebSphere 6.0+應用服務器事務管理器的適配器轰豆,此適配器用于對應用服務器提供的高級事務的支持胰伍;
- WebLogicJtaTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的對WebLogic 8.1+應用服務器事務管理器的適配器酸休,此適配器用于對應用服務器提供的高級事務的支持骂租。
Spring不僅提供這些事務管理器,還提供對如JMS事務管理的管理器等斑司,Spring提供一致的事務抽象如圖9-1所示渗饮。
圖9-1 Spring事務管理器
接下來讓我們學習一下如何在Spring配置文件中定義事務管理器:
一、聲明對本地事務的支持:
a)JDBC及iBATIS宿刮、MyBatis框架事務管理器
java代碼:
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
通過dataSource屬性指定需要事務管理的單個javax.sql.DataSource對象互站。
b)Jdo事務管理器
java代碼:
<bean id="txManager" class="org.springframework.orm.jdo.JdoTransactionManager">
<property name="persistenceManagerFactory" ref="persistenceManagerFactory"/>
</bean>
通過persistenceManagerFactory屬性指定需要事務管理的javax.jdo.PersistenceManagerFactory對象。
c)Jpa事務管理器
java代碼:
bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
通過entityManagerFactory屬性指定需要事務管理的javax.persistence.EntityManagerFactory對象僵缺。
還需要為entityManagerFactory對象指定jpaDialect屬性胡桃,該屬性所對應的對象指定了如何獲取連接對象、開啟事務磕潮、關閉事務等事務管理相關的行為翠胰。
java代碼:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
……
<property name="jpaDialect" ref="jpaDialect"/>
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
d)Hibernate事務管理器
java代碼:
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
通過entityManagerFactory屬性指定需要事務管理的org.hibernate.SessionFactory對象懊纳。
聲明式事務
聲明式事務概述
從上節(jié)編程式實現(xiàn)事務管理可以深刻體會到編程式事務的痛苦,即使通過代理配置方式也是不小的工作量亡容。
本節(jié)將介紹聲明式事務支持嗤疯,使用該方式后最大的獲益是簡單,事務管理不再是令人痛苦的闺兢,而且此方式屬于無侵入式茂缚,對業(yè)務邏輯實現(xiàn)無影響。
接下來先來看看聲明式事務如何實現(xiàn)吧屋谭。
聲明式實現(xiàn)事務管理
1脚囊、定義業(yè)務邏輯實現(xiàn),此處使用ConfigUserServiceImpl和ConfigAddressServiceImpl:
2桐磁、定義配置文件(chapter9/service/ applicationContext-service-declare.xml):
2.1悔耘、XML命名空間定義,定義用于事務支持的tx命名空間和AOP支持的aop命名空間:
java代碼:
<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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
2.2我擂、業(yè)務實現(xiàn)配置衬以,非常簡單,使用以前定義的非侵入式業(yè)務實現(xiàn):
java代碼:
<bean id="userService" class="cn.javass.spring.chapter9.service.impl.ConfigUserServiceImpl">
<property name="userDao" ref="userDao"/>
<property name="addressService" ref="addressService"/>
</bean>
<bean id="addressService" class="cn.javass.spring.chapter9.service.impl.ConfigAddressServiceImpl">
<property name="addressDao" ref="addressDao"/>
</bean>
2.3校摩、事務相關配置:
java代碼:
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
<tx:method name="*" propagation="REQUIRED" isolation="READ_COMMITTED" read-only="true"/>
</tx:attributes>
</tx:advice>
java代碼:
<aop:config>
<aop:pointcut id="serviceMethod" expression="execution(* cn..chapter9.service..*.*(..))"/>
<aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice"/>
</aop:config>
<tx:advice>:事務通知定義看峻,用于指定事務屬性,其中“transaction-manager”屬性指定事務管理器衙吩,并通過< tx:attributes >指定具體需要攔截的方法互妓;
<tx:method name=”save*”>:表示將攔截以save開頭的方法,被攔截的方法將應用配置的事務屬性:propagation=”REQUIRED”表示傳播行為是Required坤塞,isolation=”READ_COMMITTED”表示隔離級別是提交讀冯勉;
<tx:method name=”*”>:表示將攔截其他所有方法,被攔截的方法將應用配置的事務屬性:propagation=”REQUIRED”表示傳播行為是Required摹芙,isolation=”READ_COMMITTED”表示隔離級別是提交讀灼狰,read-only=”true”表示事務只讀;
<aop:config>:AOP相關配置:
<aop:pointcut/>:切入點定義瘫辩,定義名為”serviceMethod”的aspectj切入點伏嗜,切入點表達式為”execution(* cn..chapter9.service..*.*(..))”表示攔截cn包及子包下的chapter9. service包及子包下的任何類的任何方法;
<aop:advisor>:Advisor定義伐厌,其中切入點為serviceMethod,通知為txAdvice裸影。
從配置中可以看出挣轨,將對cn包及子包下的chapter9. service包及子包下的任何類的任何方法應用“txAdvice”通知指定的事務屬性。
3轩猩、修改測試方法并測試該配置方式是否好用:
將TransactionTest 類的testServiceTransaction測試方法拷貝一份命名為testDeclareTransaction:
并在testDeclareTransaction測試方法內(nèi)將:
4、執(zhí)行測試涯贞,測試正常通過雏搂,說明該方式能正常工作,當調(diào)用save方法時將匹配到事務通知中定義的“<tx:method name=”save”>”中指定的事務屬性摩幔,而調(diào)用countAll方法時將匹配到事務通知中定義的“<tx:method name=””>”中指定的事務屬性。
聲明式事務是如何實現(xiàn)事務管理的呢鞭铆?還記不記得TransactionProxyFactoryBean實現(xiàn)配置式事務管理或衡,配置式事務管理是通過代理方式實現(xiàn),而聲明式事務管理同樣是通過AOP代理方式實現(xiàn)车遂。
聲明式事務通過AOP代理方式實現(xiàn)事務管理封断,利用環(huán)繞通知TransactionInterceptor實現(xiàn)事務的開啟及關閉,而TransactionProxyFactoryBean內(nèi)部也是通過該環(huán)繞通知實現(xiàn)的舶担,因此可以認為是<tx:tags/>幫你定義了TransactionProxyFactoryBean坡疼,從而簡化事務管理。
了解了實現(xiàn)方式后衣陶,接下來詳細學習一下配置吧:
9.4.4 <tx:advice/>配置詳解
聲明式事務管理通過配置<tx:advice/>來定義事務屬性柄瑰,配置方式如下所示:
java代碼:
<tx:advice id="……" transaction-manager="……">
<tx:attributes>
<tx:method name="……"
propagation=" REQUIRED"
isolation="READ_COMMITTED"
timeout="-1"
read-only="false"
no-rollback-for=""
rollback-for=""/>
……
</tx:attributes>
</tx:advice>
<tx:advice>:id用于指定此通知的名字, transaction-manager用于指定事務管理器剪况,默認的事務管理器名字為“transactionManager”狱意;
<tx:method>:用于定義事務屬性即相關聯(lián)的方法名;
name:定義與事務屬性相關聯(lián)的方法名拯欧,將對匹配的方法應用定義的事務屬性详囤,可以使用“”通配符來匹配一組或所有方法,如“save”將匹配以save開頭的方法镐作,而“*”將匹配所有方法藏姐;
propagation:事務傳播行為定義,默認為“REQUIRED”该贾,表示Required羔杨,其值可以通過TransactionDefinition的靜態(tài)傳播行為變量的“PROPAGATION_”后邊部分指定,如“TransactionDefinition.PROPAGATION_REQUIRED”可以使用“REQUIRED”指定杨蛋;
isolation:事務隔離級別定義兜材;默認為“DEFAULT”,其值可以通過TransactionDefinition的靜態(tài)隔離級別變量的“ISOLATION_”后邊部分指定逞力,如“TransactionDefinition. ISOLATION_DEFAULT”可以使用“DEFAULT”指定:
timeout:事務超時時間設置曙寡,單位為秒,默認-1寇荧,表示事務超時將依賴于底層事務系統(tǒng)举庶;
read-only:事務只讀設置,默認為false揩抡,表示不是只讀户侥;
rollback-for:需要觸發(fā)回滾的異常定義镀琉,以“,”分割蕊唐,默認任何RuntimeException 將導致事務回滾屋摔,而任何Checked Exception 將不導致事務回滾;異常名字定義和TransactionProxyFactoryBean中含義一樣
no-rollback-for:不被觸發(fā)進行回滾的 Exception(s)替梨;以“钓试,”分割;異常名字定義和TransactionProxyFactoryBean中含義一樣耙替;
記不記得在配置方式中為了解決“自我調(diào)用”而導致的不能設置正確的事務屬性問題亚侠,使用“((IUserService)AopContext.currentProxy()).otherTransactionMethod()”方式解決,在聲明式事務要得到支持需要使用<aop:config expose-proxy=”true”>來開啟俗扇。
9.4.5 多事務語義配置及最佳實踐
什么是多事務語義硝烂?說白了就是為不同的Bean配置不同的事務屬性,因為我們項目中不可能就幾個Bean铜幽,而可能很多滞谢,這可能需要為Bean分組,為不同組的Bean配置不同的事務語義除抛。在Spring中狮杨,可以通過配置多切入點和多事務通知并通過不同方式組合使用即可。
1到忽、首先看下聲明式事務配置的最佳實踐吧:
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="create*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="merge*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="put*" propagation="REQUIRED" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
<tx:method name="count*" propagation="SUPPORTS" read-only="true" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="list*" propagation="SUPPORTS" read-only="true" />
<tx:method name="*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* cn.javass..service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
</aop:config>
該聲明式事務配置可以應付常見的CRUD接口定義橄教,并實現(xiàn)事務管理,我們只需修改切入點表達式來攔截我們的業(yè)務實現(xiàn)從而對其應用事務屬性就可以了喘漏,如果還有更復雜的事務屬性直接添加即可护蝶,即
如果我們有一個batchSaveOrUpdate方法需要“REQUIRES_NEW”事務傳播行為,則直接添加如下配置即可:
java代碼:
1
<tx:method name="batchSaveOrUpdate" propagation="REQUIRES_NEW" />
2翩迈、接下來看一下多事務語義配置吧持灰,聲明式事務最佳實踐中已經(jīng)配置了通用事務屬性,因此可以針對需要其他事務屬性的業(yè)務方法進行特例化配置:
java代碼:
<tx:advice id="noTxAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="*" propagation="NEVER" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="noTxPointcut" expression="execution(* cn.javass..util.*.*())" />
<aop:advisor advice-ref="noTxPointcut" pointcut-ref="noTxAdvice" />
</aop:config>
該聲明將對切入點匹配的方法所在事務應用“Never”傳播行為负饲。
多事務語義配置時堤魁,切入點一定不要疊加,否則將應用兩次事務屬性返十,造成不必要的錯誤及麻煩妥泉。
@Transactional實現(xiàn)事務管理
對聲明式事務管理,Spring提供基于@Transactional注解方式來實現(xiàn)吧慢,但需要Java 5+涛漂。
注解方式是最簡單的事務配置方式,可以直接在Java源代碼中聲明事務屬性检诗,且對于每一個業(yè)務類或方法如果需要事務都必須使用此注解匈仗。
接下來學習一下注解事務的使用吧:
1、定義業(yè)務邏輯實現(xiàn):
package cn.javass.spring.chapter9.service.impl;
//省略import
public class AnnotationUserServiceImpl implements IUserService {
private IUserDao userDao;
private IAddressService addressService;
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
public void setAddressService(IAddressService addressService) {
this.addressService = addressService;
}
@Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED)
@Override
public void save(final UserModel user) {
userDao.save(user);
user.getAddress().setUserId(user.getId());
addressService.save(user.getAddress());
}
@Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED, readOnly=true)
@Override
public int countAll() {
return userDao.countAll();
}
}
2逢慌、定義配置文件(chapter9/service/ applicationContext-service-annotation.xml):
2.1悠轩、XML命名空間定義,定義用于事務支持的tx命名空間和AOP支持的aop命名空間:
java代碼:
<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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
2.2攻泼、業(yè)務實現(xiàn)配置火架,非常簡單,使用以前定義的非侵入式業(yè)務實現(xiàn):
java代碼:
<bean id="userService" class="cn.javass.spring.chapter9.service.impl.ConfigUserServiceImpl">
<property name="userDao" ref="userDao"/>
<property name="addressService" ref="addressService"/>
</bean>
<bean id="addressService" class="cn.javass.spring.chapter9.service.impl.ConfigAddressServiceImpl">
<property name="addressDao" ref="addressDao"/>
</bean>
2.3忙菠、事務相關配置:
java代碼:
1
<tx:annotation-driven transaction-manager="txManager"/>
使用如上配置已支持聲明式事務何鸡。
3、修改測試方法并測試該配置方式是否好用:
將TransactionTest 類的testServiceTransaction測試方法拷貝一份命名為testAnntationTransactionTest:
classpath:chapter9/service/applicationContext-service-annotation.xml"
userService.save(user);
try {
userService.save(user);
Assert.fail();
} catch (RuntimeException e) {
}
Assert.assertEquals(0, userService.countAll());
Assert.assertEquals(0, addressService.countAll());
4牛欢、執(zhí)行測試骡男,測試正常通過,說明該方式能正常工作傍睹,因為在AnnotationAddressServiceImpl類的save方法中拋出異常隔盛,因此事務需要回滾,所以兩個countAll操作都返回0拾稳。
9.4.7 @Transactional配置詳解
Spring提供的<tx:annotation-driven/>用于開啟對注解事務管理的支持吮炕,從而能識別Bean類上的@Transactional注解元數(shù)據(jù),其具有以下屬性:
transaction-manager:指定事務管理器名字访得,默認為transactionManager龙亲,當使用其他名字時需要明確指定;
proxy-target-class:表示將使用的代碼機制悍抑,默認false表示使用JDK代理鳄炉,如果為true將使用CGLIB代理
order:定義事務通知順序,默認Ordered.LOWEST_PRECEDENCE传趾,表示將順序決定權交給AOP來處理迎膜。
Spring使用@Transactional 來指定事務屬性,可以在接口浆兰、類或方法上指定磕仅,如果類和方法上都指定了@Transactional ,則方法上的事務屬性被優(yōu)先使用簸呈,具體屬性如下:
value:指定事務管理器名字榕订,默認使用<tx:annotation-driven/>指定的事務管理器,用于支持多事務管理器環(huán)境蜕便;
propagation:指定事務傳播行為劫恒,默認為Required,使用Propagation.REQUIRED指定;
isolation:指定事務隔離級別两嘴,默認為“DEFAULT”丛楚,使用Isolation.DEFAULT指定;
readOnly:指定事務是否只讀憔辫,默認false表示事務非只讀趣些;
timeout:指定事務超時時間,以秒為單位贰您,默認-1表示事務超時將依賴于底層事務系統(tǒng)坏平;
rollbackFor:指定一組異常類,遇到該類異常將回滾事務锦亦;
rollbackForClassname:指定一組異常類名字舶替,其含義與<tx:method>中的rollback-for屬性語義完全一樣;
noRollbackFor:指定一組異常類杠园,即使遇到該類異常也將提交事務顾瞪,即不回滾事務;
noRollbackForClassname:指定一組異常類名字返劲,其含義與<tx:method>中的no-rollback-for屬性語義完全一樣玲昧;
Spring提供的@Transactional 注解事務管理內(nèi)部同樣利用環(huán)繞通知TransactionInterceptor實現(xiàn)事務的開啟及關閉。
使用@Transactional注解事務管理需要特別注意以下幾點:
如果在接口篮绿、實現(xiàn)類或方法上都指定了@Transactional 注解孵延,則優(yōu)先級順序為方法>實現(xiàn)類>接口;
建議只在實現(xiàn)類或?qū)崿F(xiàn)類的方法上使用@Transactional亲配,而不要在接口上使用尘应,這是因為如果使用JDK代理機制是沒問題,因為其使用基于接口的代理吼虎;而使用使用CGLIB代理機制時就會遇到問題犬钢,因為其使用基于類的代理而不是接口,這是因為接口上的@Transactional注解是“不能繼承的”思灰;
具體請參考基于JDK動態(tài)代理和CGLIB動態(tài)代理的實現(xiàn)Spring注解管理事務(@Trasactional)到底有什么區(qū)別玷犹。
在Spring代理機制下(不管是JDK動態(tài)代理還是CGLIB代理),“自我調(diào)用”同樣不會應用相應的事務屬性洒疚,其語義和<tx:tags>中一樣歹颓;
默認只對RuntimeException異常回滾油湖;
在使用Spring代理時巍扛,默認只有在public可見度的方法的@Transactional 注解才是有效的,其它可見度(protected乏德、private撤奸、包可見)的方法上即使有@Transactional 注解也不會應用這些事務屬性的,Spring也不會報錯,如果你非要使用非公共方法注解事務管理的話胧瓜,可考慮使用AspectJ矢棚。
微信公眾號【黃小斜】作者是螞蟻金服 JAVA 工程師,專注于 JAVA
后端技術棧:SpringBoot贷痪、SSM全家桶幻妓、MySQL蹦误、分布式劫拢、中間件、微服務强胰,同時也懂點投資理財舱沧,堅持學習和寫作,相信終身學習的力量偶洋!關注公眾號后回復”架構師“即可領取
Java基礎熟吏、進階、項目和架構師等免費學習資料玄窝,更有數(shù)據(jù)庫牵寺、分布式、微服務等熱門技術學習視頻恩脂,內(nèi)容豐富帽氓,兼顧原理和實踐,另外也將贈送作者原創(chuàng)的Java學習指南俩块、Java程序員面試指南等干貨資源