原文出處: 張開(kāi)濤
數(shù)據(jù)庫(kù)事務(wù)概述
事務(wù)首先是一系列操作組成的工作單元,該工作單元內(nèi)的操作是不可分割的,即要么所有操作都做,要么所有操作都不做,這就是事務(wù)。
事務(wù)必需滿(mǎn)足ACID(原子性、一致性、隔離性和持久性)特性,缺一不可:
- 原子性(Atomicity):即事務(wù)是不可分割的最小工作單元,事務(wù)內(nèi)的操作要么全做,要么全不做;
- 一致性(Consistency):在事務(wù)執(zhí)行前數(shù)據(jù)庫(kù)的數(shù)據(jù)處于正確的狀態(tài),而事務(wù)執(zhí)行完成后數(shù)據(jù)庫(kù)的數(shù)據(jù)還是處于正確的狀態(tài),即數(shù)據(jù)完整性約束沒(méi)有被破壞;如銀行轉(zhuǎn)帳,A轉(zhuǎn)帳給B,必須保證A的錢(qián)一定轉(zhuǎn)給B,一定不會(huì)出現(xiàn)A的錢(qián)轉(zhuǎn)了但B沒(méi)收到,否則數(shù)據(jù)庫(kù)的數(shù)據(jù)就處于不一致(不正確)的狀態(tài)。
- 隔離性(Isolation):并發(fā)事務(wù)執(zhí)行之間無(wú)影響,在一個(gè)事務(wù)內(nèi)部的操作對(duì)其他事務(wù)是不產(chǎn)生影響,這需要事務(wù)隔離級(jí)別來(lái)指定隔離性;
- 持久性(Durability):事務(wù)一旦執(zhí)行成功,它對(duì)數(shù)據(jù)庫(kù)的數(shù)據(jù)的改變必須是永久的,不會(huì)因比如遇到系統(tǒng)故障或斷電造成數(shù)據(jù)不一致或丟失。
在實(shí)際項(xiàng)目開(kāi)發(fā)中數(shù)據(jù)庫(kù)操作一般都是并發(fā)執(zhí)行的,即有多個(gè)事務(wù)并發(fā)執(zhí)行,并發(fā)執(zhí)行就可能遇到問(wèn)題,目前常見(jiàn)的問(wèn)題如下:
- 丟失更新:兩個(gè)事務(wù)同時(shí)更新一行數(shù)據(jù),最后一個(gè)事務(wù)的更新會(huì)覆蓋掉第一個(gè)事務(wù)的更新,從而導(dǎo)致第一個(gè)事務(wù)更新的數(shù)據(jù)丟失,這是由于沒(méi)有加鎖造成的;
- 臟讀:一個(gè)事務(wù)看到了另一個(gè)事務(wù)未提交的更新數(shù)據(jù);
- 不可重復(fù)讀:在同一事務(wù)中,多次讀取同一數(shù)據(jù)卻返回不同的結(jié)果;也就是有其他事務(wù)更改了這些數(shù)據(jù);
- 幻讀:一個(gè)事務(wù)在執(zhí)行過(guò)程中讀取到了另一個(gè)事務(wù)已提交的插入數(shù)據(jù);即在第一個(gè)事務(wù)開(kāi)始時(shí)讀取到一批數(shù)據(jù),但此后另一個(gè)事務(wù)又插入了新數(shù)據(jù)并提交,此時(shí)第一個(gè)事務(wù)又讀取這批數(shù)據(jù)但發(fā)現(xiàn)多了一條,即好像發(fā)生幻覺(jué)一樣。
為了解決這些并發(fā)問(wèn)題,需要通過(guò)數(shù)據(jù)庫(kù)隔離級(jí)別來(lái)解決,在標(biāo)準(zhǔn)SQL規(guī)范中定義了四種隔離級(jí)別:
- 未提交讀(Read Uncommitted):最低隔離級(jí)別,一個(gè)事務(wù)能讀取到別的事務(wù)未提交的更新數(shù)據(jù),很不安全,可能出現(xiàn)丟失更新、臟讀、不可重復(fù)讀、幻讀;
- 提交讀(Read Committed):一個(gè)事務(wù)能讀取到別的事務(wù)提交的更新數(shù)據(jù),不能看到未提交的更新數(shù)據(jù),不可能可能出現(xiàn)丟失更新、臟讀,但可能出現(xiàn)不可重復(fù)讀、幻讀;
- 可重復(fù)讀(Repeatable Read):保證同一事務(wù)中先后執(zhí)行的多次查詢(xún)將返回同一結(jié)果,不受其他事務(wù)影響,可能可能出現(xiàn)丟失更新、臟讀、不可重復(fù)讀,但可能出現(xiàn)幻讀;
- 序列化(Serializable):最高隔離級(jí)別,不允許事務(wù)并發(fā)執(zhí)行,而必須串行化執(zhí)行,最安全,不可能出現(xiàn)更新、臟讀、不可重復(fù)讀、幻讀。
隔離級(jí)別越高,數(shù)據(jù)庫(kù)事務(wù)并發(fā)執(zhí)行性能越差,能處理的操作越少。因此在實(shí)際項(xiàng)目開(kāi)發(fā)中為了考慮并發(fā)性能一般使用提交讀隔離級(jí)別,它能避免丟失更新和臟讀,盡管不可重復(fù)讀和幻讀不能避免,但可以在可能出現(xiàn)的場(chǎng)合使用悲觀鎖或樂(lè)觀鎖來(lái)解決這些問(wèn)題。
事務(wù)類(lèi)型
數(shù)據(jù)庫(kù)事務(wù)類(lèi)型有本地事務(wù)和分布式事務(wù):
- 本地事務(wù):就是普通事務(wù),能保證單臺(tái)數(shù)據(jù)庫(kù)上的操作的ACID,被限定在一臺(tái)數(shù)據(jù)庫(kù)上;
- 分布式事務(wù):涉及兩個(gè)或多個(gè)數(shù)據(jù)庫(kù)源的事務(wù),即跨越多臺(tái)同類(lèi)或異類(lèi)數(shù)據(jù)庫(kù)的事務(wù)(由每臺(tái)數(shù)據(jù)庫(kù)的本地事務(wù)組成的),分布式事務(wù)旨在保證這些本地事務(wù)的所有操作的ACID,使事務(wù)可以跨越多臺(tái)數(shù)據(jù)庫(kù);
Java事務(wù)類(lèi)型有JDBC事務(wù)和JTA事務(wù):
- JDBC事務(wù):就是數(shù)據(jù)庫(kù)事務(wù)類(lèi)型中的本地事務(wù),通過(guò)Connection對(duì)象的控制來(lái)管理事務(wù);
- JTA事務(wù):JTA指Java事務(wù)API(Java Transaction API),是Java EE數(shù)據(jù)庫(kù)事務(wù)規(guī)范, JTA只提供了事務(wù)管理接口,由應(yīng)用程序服務(wù)器廠(chǎng)商(如WebSphere Application Server)提供實(shí)現(xiàn),JTA事務(wù)比JDBC更強(qiáng)大,支持分布式事務(wù)。
Java EE事務(wù)類(lèi)型有本地事務(wù)和全局事務(wù):
- 本地事務(wù):使用JDBC編程實(shí)現(xiàn)事務(wù);
- 全局事務(wù):由應(yīng)用程序服務(wù)器提供,使用JTA事務(wù);
按是否通過(guò)編程實(shí)現(xiàn)事務(wù)有聲明式事務(wù)和編程式事務(wù);
- 聲明式事務(wù): 通過(guò)注解或XML配置文件指定事務(wù)信息;
- 編程式事務(wù):通過(guò)編寫(xiě)代碼實(shí)現(xiàn)事務(wù)。
Spring提供的事務(wù)管理
Spring框架最核心功能之一就是事務(wù)管理,而且提供一致的事務(wù)管理抽象,這能幫助我們:
- 提供一致的編程式事務(wù)管理API,不管使用Spring JDBC框架還是集成第三方框架使用該API進(jìn)行事務(wù)編程;
- 無(wú)侵入式的聲明式事務(wù)支持。
Spring支持聲明式事務(wù)和編程式事務(wù)事務(wù)類(lèi)型。
spring事務(wù)特性
spring所有的事務(wù)管理策略類(lèi)都繼承自org.springframework.transaction.PlatformTransactionManager接口
其中TransactionDefinition接口定義以下特性:
事務(wù)隔離級(jí)別
隔離級(jí)別是指若干個(gè)并發(fā)的事務(wù)之間的隔離程度。TransactionDefinition 接口中定義了五個(gè)表示隔離級(jí)別的常量:
- TransactionDefinition.ISOLATION_DEFAULT:這是默認(rèn)值,表示使用底層數(shù)據(jù)庫(kù)的默認(rèn)隔離級(jí)別。對(duì)大部分?jǐn)?shù)據(jù)庫(kù)而言,通常這值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED:該隔離級(jí)別表示一個(gè)事務(wù)可以讀取另一個(gè)事務(wù)修改但還沒(méi)有提交的數(shù)據(jù)。該級(jí)別不能防止臟讀,不可重復(fù)讀和幻讀,因此很少使用該隔離級(jí)別。比如PostgreSQL實(shí)際上并沒(méi)有此級(jí)別。
- TransactionDefinition.ISOLATION_READ_COMMITTED:該隔離級(jí)別表示一個(gè)事務(wù)只能讀取另一個(gè)事務(wù)已經(jīng)提交的數(shù)據(jù)。該級(jí)別可以防止臟讀,這也是大多數(shù)情況下的推薦值。
- TransactionDefinition.ISOLATION_REPEATABLE_READ:該隔離級(jí)別表示一個(gè)事務(wù)在整個(gè)過(guò)程中可以多次重復(fù)執(zhí)行某個(gè)查詢(xún),并且每次返回的記錄都相同。該級(jí)別可以防止臟讀和不可重復(fù)讀。
- TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事務(wù)依次逐個(gè)執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說(shuō),該級(jí)別可以防止臟讀、不可重復(fù)讀以及幻讀。但是這將嚴(yán)重影響程序的性能。通常情況下也不會(huì)用到該級(jí)別。
事務(wù)傳播行為
所謂事務(wù)的傳播行為是指,如果在開(kāi)始當(dāng)前事務(wù)之前,一個(gè)事務(wù)上下文已經(jīng)存在,此時(shí)有若干選項(xiàng)可以指定一個(gè)事務(wù)性方法的執(zhí)行行為。在TransactionDefinition定義中包括了如下幾個(gè)表示傳播行為的常量:
- TransactionDefinition.PROPAGATION_REQUIRED:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(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)前沒(méi)有事務(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)前沒(méi)有事務(wù),則拋出異常。
- TransactionDefinition.PROPAGATION_NESTED:如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來(lái)運(yùn)行;如果當(dāng)前沒(méi)有事務(wù),則該取值等價(jià)于TransactionDefinition.PROPAGATION_REQUIRED。
事務(wù)超時(shí)
所謂事務(wù)超時(shí),就是指一個(gè)事務(wù)所允許執(zhí)行的最長(zhǎng)時(shí)間,如果超過(guò)該時(shí)間限制但事務(wù)還沒(méi)有完成,則自動(dòng)回滾事務(wù)。在 TransactionDefinition 中以 int 的值來(lái)表示超時(shí)時(shí)間,其單位是秒。
默認(rèn)設(shè)置為底層事務(wù)系統(tǒng)的超時(shí)值,如果底層數(shù)據(jù)庫(kù)事務(wù)系統(tǒng)沒(méi)有設(shè)置超時(shí)值,那么就是none,沒(méi)有超時(shí)限制。
事務(wù)只讀屬性
只讀事務(wù)用于客戶(hù)代碼只讀但不修改數(shù)據(jù)的情形,只讀事務(wù)用于特定情景下的優(yōu)化,比如使用Hibernate的時(shí)候。
默認(rèn)為讀寫(xiě)事務(wù)。
概述
Spring框架支持事務(wù)管理的核心是事務(wù)管理器抽象,對(duì)于不同的數(shù)據(jù)訪(fǎng)問(wèn)框架(如Hibernate)通過(guò)實(shí)現(xiàn)策略接口PlatformTransactionManager,從而能支持各種數(shù)據(jù)訪(fǎng)問(wèn)框架的事務(wù)管理,PlatformTransactionManager接口定義如下:
java代碼:
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
- getTransaction():返回一個(gè)已經(jīng)激活的事務(wù)或創(chuàng)建一個(gè)新的事務(wù)(根據(jù)給定的TransactionDefinition類(lèi)型參數(shù)定義的事務(wù)屬性),返回的是TransactionStatus對(duì)象代表了當(dāng)前事務(wù)的狀態(tài),其中該方法拋出TransactionException(未檢查異常)表示事務(wù)由于某種原因失敗。
- commit():用于提交TransactionStatus參數(shù)代表的事務(wù),具體語(yǔ)義請(qǐng)參考Spring Javadoc;
- rollback():用于回滾TransactionStatus參數(shù)代表的事務(wù),具體語(yǔ)義請(qǐng)參考Spring Javadoc。
TransactionDefinition接口定義如下:
java代碼:
public interface TransactionDefinition {
int getPropagationBehavior();
int getIsolationLevel();
int getTimeout();
boolean isReadOnly();
String getName();
}
- getPropagationBehavior():返回定義的事務(wù)傳播行為;
- getIsolationLevel():返回定義的事務(wù)隔離級(jí)別;
- getTimeout():返回定義的事務(wù)超時(shí)時(shí)間;
- isReadOnly():返回定義的事務(wù)是否是只讀的;
- getName():返回定義的事務(wù)名字。
TransactionStatus接口定義如下:
java代碼:
public interface TransactionStatus extends SavepointManager {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
}
- isNewTransaction():返回當(dāng)前事務(wù)狀態(tài)是否是新事務(wù);
- hasSavepoint():返回當(dāng)前事務(wù)是否有保存點(diǎn);
- setRollbackOnly():設(shè)置當(dāng)前事務(wù)應(yīng)該回滾;
- isRollbackOnly(():返回當(dāng)前事務(wù)是否應(yīng)該回滾;
- flush():用于刷新底層會(huì)話(huà)中的修改到數(shù)據(jù)庫(kù),一般用于刷新如Hibernate/JPA的會(huì)話(huà),可能對(duì)如JDBC類(lèi)型的事務(wù)無(wú)任何影響;
- isCompleted():當(dāng)前事務(wù)否已經(jīng)完成。
內(nèi)置事務(wù)管理器實(shí)現(xiàn)
Spring提供了許多內(nèi)置事務(wù)管理器實(shí)現(xiàn):
- DataSourceTransactionManager:位于org.springframework.jdbc.datasource包中,數(shù)據(jù)源事務(wù)管理器,提供對(duì)單個(gè)javax.sql.DataSource事務(wù)管理,用于Spring JDBC抽象框架、iBATIS或MyBatis框架的事務(wù)管理;
- JdoTransactionManager:位于org.springframework.orm.jdo包中,提供對(duì)單個(gè)javax.jdo.PersistenceManagerFactory事務(wù)管理,用于集成JDO框架時(shí)的事務(wù)管理;
- JpaTransactionManager:位于org.springframework.orm.jpa包中,提供對(duì)單個(gè)javax.persistence.EntityManagerFactory事務(wù)支持,用于集成JPA實(shí)現(xiàn)框架時(shí)的事務(wù)管理;
- HibernateTransactionManager:位于org.springframework.orm.hibernate3包中,提供對(duì)單個(gè)org.hibernate.SessionFactory事務(wù)支持,用于集成Hibernate框架時(shí)的事務(wù)管理;該事務(wù)管理器只支持Hibernate3+版本,且Spring3.0+版本只支持Hibernate 3.2+版本;
- JtaTransactionManager:位于org.springframework.transaction.jta包中,提供對(duì)分布式事務(wù)管理的支持,并將事務(wù)管理委托給Java EE應(yīng)用服務(wù)器事務(wù)管理器;
- OC4JjtaTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的對(duì)OC4J10.1.3+應(yīng)用服務(wù)器事務(wù)管理器的適配器,此適配器用于對(duì)應(yīng)用服務(wù)器提供的高級(jí)事務(wù)的支持;
- WebSphereUowTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的對(duì)WebSphere 6.0+應(yīng)用服務(wù)器事務(wù)管理器的適配器,此適配器用于對(duì)應(yīng)用服務(wù)器提供的高級(jí)事務(wù)的支持;
- WebLogicJtaTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的對(duì)WebLogic 8.1+應(yīng)用服務(wù)器事務(wù)管理器的適配器,此適配器用于對(duì)應(yīng)用服務(wù)器提供的高級(jí)事務(wù)的支持。
Spring不僅提供這些事務(wù)管理器,還提供對(duì)如JMS事務(wù)管理的管理器等,Spring提供一致的事務(wù)抽象如圖9-1所示。
圖9-1 Spring事務(wù)管理器
接下來(lái)讓我們學(xué)習(xí)一下如何在Spring配置文件中定義事務(wù)管理器:
一、聲明對(duì)本地事務(wù)的支持:
a)JDBC及iBATIS、MyBatis框架事務(wù)管理器
java代碼:
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
通過(guò)dataSource屬性指定需要事務(wù)管理的單個(gè)javax.sql.DataSource對(duì)象。
b)Jdo事務(wù)管理器
java代碼:
<bean id="txManager" class="org.springframework.orm.jdo.JdoTransactionManager">
<property name="persistenceManagerFactory" ref="persistenceManagerFactory"/>
</bean>
通過(guò)persistenceManagerFactory屬性指定需要事務(wù)管理的javax.jdo.PersistenceManagerFactory對(duì)象。
c)Jpa事務(wù)管理器
java代碼:
bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
通過(guò)entityManagerFactory屬性指定需要事務(wù)管理的javax.persistence.EntityManagerFactory對(duì)象。
還需要為entityManagerFactory對(duì)象指定jpaDialect屬性,該屬性所對(duì)應(yīng)的對(duì)象指定了如何獲取連接對(duì)象、開(kāi)啟事務(wù)、關(guān)閉事務(wù)等事務(wù)管理相關(guān)的行為。
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事務(wù)管理器
java代碼:
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
通過(guò)entityManagerFactory屬性指定需要事務(wù)管理的org.hibernate.SessionFactory對(duì)象。
聲明式事務(wù)
聲明式事務(wù)概述
從上節(jié)編程式實(shí)現(xiàn)事務(wù)管理可以深刻體會(huì)到編程式事務(wù)的痛苦,即使通過(guò)代理配置方式也是不小的工作量。
本節(jié)將介紹聲明式事務(wù)支持,使用該方式后最大的獲益是簡(jiǎn)單,事務(wù)管理不再是令人痛苦的,而且此方式屬于無(wú)侵入式,對(duì)業(yè)務(wù)邏輯實(shí)現(xiàn)無(wú)影響。
接下來(lái)先來(lái)看看聲明式事務(wù)如何實(shí)現(xiàn)吧。
聲明式實(shí)現(xiàn)事務(wù)管理
1、定義業(yè)務(wù)邏輯實(shí)現(xiàn),此處使用ConfigUserServiceImpl和ConfigAddressServiceImpl:
2、定義配置文件(chapter9/service/ applicationContext-service-declare.xml):
2.1、XML命名空間定義,定義用于事務(wù)支持的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è)務(wù)實(shí)現(xiàn)配置,非常簡(jiǎn)單,使用以前定義的非侵入式業(yè)務(wù)實(shí)現(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、事務(wù)相關(guān)配置:
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>:事務(wù)通知定義,用于指定事務(wù)屬性,其中“transaction-manager”屬性指定事務(wù)管理器,并通過(guò)< tx:attributes >指定具體需要攔截的方法;
<tx:method name=”save*”>:表示將攔截以save開(kāi)頭的方法,被攔截的方法將應(yīng)用配置的事務(wù)屬性:propagation=”REQUIRED”表示傳播行為是Required,isolation=”READ_COMMITTED”表示隔離級(jí)別是提交讀;
<tx:method name=”*”>:表示將攔截其他所有方法,被攔截的方法將應(yīng)用配置的事務(wù)屬性:propagation=”REQUIRED”表示傳播行為是Required,isolation=”READ_COMMITTED”表示隔離級(jí)別是提交讀,read-only=”true”表示事務(wù)只讀;
<aop:config>:AOP相關(guān)配置:
<aop:pointcut/>:切入點(diǎn)定義,定義名為”serviceMethod”的aspectj切入點(diǎn),切入點(diǎn)表達(dá)式為”execution(* cn..chapter9.service..*.*(..))”表示攔截cn包及子包下的chapter9. service包及子包下的任何類(lèi)的任何方法;
<aop:advisor>:Advisor定義,其中切入點(diǎn)為serviceMethod,通知為txAdvice。
從配置中可以看出,將對(duì)cn包及子包下的chapter9. service包及子包下的任何類(lèi)的任何方法應(yīng)用“txAdvice”通知指定的事務(wù)屬性。
3、修改測(cè)試方法并測(cè)試該配置方式是否好用:
將TransactionTest 類(lèi)的testServiceTransaction測(cè)試方法拷貝一份命名為testDeclareTransaction:
并在testDeclareTransaction測(cè)試方法內(nèi)將:
4、執(zhí)行測(cè)試,測(cè)試正常通過(guò),說(shuō)明該方式能正常工作,當(dāng)調(diào)用save方法時(shí)將匹配到事務(wù)通知中定義的“<tx:method name=”save”>”中指定的事務(wù)屬性,而調(diào)用countAll方法時(shí)將匹配到事務(wù)通知中定義的“<tx:method name=””>”中指定的事務(wù)屬性。
聲明式事務(wù)是如何實(shí)現(xiàn)事務(wù)管理的呢?還記不記得TransactionProxyFactoryBean實(shí)現(xiàn)配置式事務(wù)管理,配置式事務(wù)管理是通過(guò)代理方式實(shí)現(xiàn),而聲明式事務(wù)管理同樣是通過(guò)AOP代理方式實(shí)現(xiàn)。
聲明式事務(wù)通過(guò)AOP代理方式實(shí)現(xiàn)事務(wù)管理,利用環(huán)繞通知TransactionInterceptor實(shí)現(xiàn)事務(wù)的開(kāi)啟及關(guān)閉,而TransactionProxyFactoryBean內(nèi)部也是通過(guò)該環(huán)繞通知實(shí)現(xiàn)的,因此可以認(rèn)為是<tx:tags/>幫你定義了TransactionProxyFactoryBean,從而簡(jiǎn)化事務(wù)管理。
了解了實(shí)現(xiàn)方式后,接下來(lái)詳細(xì)學(xué)習(xí)一下配置吧:
9.4.4 <tx:advice/>配置詳解
聲明式事務(wù)管理通過(guò)配置<tx:advice/>來(lái)定義事務(wù)屬性,配置方式如下所示:
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用于指定事務(wù)管理器,默認(rèn)的事務(wù)管理器名字為“transactionManager”;
<tx:method>:用于定義事務(wù)屬性即相關(guān)聯(lián)的方法名;
name:定義與事務(wù)屬性相關(guān)聯(lián)的方法名,將對(duì)匹配的方法應(yīng)用定義的事務(wù)屬性,可以使用“”通配符來(lái)匹配一組或所有方法,如“save”將匹配以save開(kāi)頭的方法,而“*”將匹配所有方法;
propagation:事務(wù)傳播行為定義,默認(rèn)為“REQUIRED”,表示Required,其值可以通過(guò)TransactionDefinition的靜態(tài)傳播行為變量的“PROPAGATION_”后邊部分指定,如“TransactionDefinition.PROPAGATION_REQUIRED”可以使用“REQUIRED”指定;
isolation:事務(wù)隔離級(jí)別定義;默認(rèn)為“DEFAULT”,其值可以通過(guò)TransactionDefinition的靜態(tài)隔離級(jí)別變量的“ISOLATION_”后邊部分指定,如“TransactionDefinition. ISOLATION_DEFAULT”可以使用“DEFAULT”指定:
timeout:事務(wù)超時(shí)時(shí)間設(shè)置,單位為秒,默認(rèn)-1,表示事務(wù)超時(shí)將依賴(lài)于底層事務(wù)系統(tǒng);
read-only:事務(wù)只讀設(shè)置,默認(rèn)為false,表示不是只讀;
rollback-for:需要觸發(fā)回滾的異常定義,以“,”分割,默認(rèn)任何RuntimeException 將導(dǎo)致事務(wù)回滾,而任何Checked Exception 將不導(dǎo)致事務(wù)回滾;異常名字定義和TransactionProxyFactoryBean中含義一樣
no-rollback-for:不被觸發(fā)進(jìn)行回滾的 Exception(s);以“,”分割;異常名字定義和TransactionProxyFactoryBean中含義一樣;
記不記得在配置方式中為了解決“自我調(diào)用”而導(dǎo)致的不能設(shè)置正確的事務(wù)屬性問(wèn)題,使用“((IUserService)AopContext.currentProxy()).otherTransactionMethod()”方式解決,在聲明式事務(wù)要得到支持需要使用<aop:config expose-proxy=”true”>來(lái)開(kāi)啟。
9.4.5 多事務(wù)語(yǔ)義配置及最佳實(shí)踐
什么是多事務(wù)語(yǔ)義?說(shuō)白了就是為不同的Bean配置不同的事務(wù)屬性,因?yàn)槲覀冺?xiàng)目中不可能就幾個(gè)Bean,而可能很多,這可能需要為Bean分組,為不同組的Bean配置不同的事務(wù)語(yǔ)義。在Spring中,可以通過(guò)配置多切入點(diǎn)和多事務(wù)通知并通過(guò)不同方式組合使用即可。
1、首先看下聲明式事務(wù)配置的最佳實(shí)踐吧:
<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>
該聲明式事務(wù)配置可以應(yīng)付常見(jiàn)的CRUD接口定義,并實(shí)現(xiàn)事務(wù)管理,我們只需修改切入點(diǎn)表達(dá)式來(lái)攔截我們的業(yè)務(wù)實(shí)現(xiàn)從而對(duì)其應(yīng)用事務(wù)屬性就可以了,如果還有更復(fù)雜的事務(wù)屬性直接添加即可,即
如果我們有一個(gè)batchSaveOrUpdate方法需要“REQUIRES_NEW”事務(wù)傳播行為,則直接添加如下配置即可:
java代碼:
1
<tx:method name="batchSaveOrUpdate" propagation="REQUIRES_NEW" />
2、接下來(lái)看一下多事務(wù)語(yǔ)義配置吧,聲明式事務(wù)最佳實(shí)踐中已經(jīng)配置了通用事務(wù)屬性,因此可以針對(duì)需要其他事務(wù)屬性的業(yè)務(wù)方法進(jìn)行特例化配置:
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>
該聲明將對(duì)切入點(diǎn)匹配的方法所在事務(wù)應(yīng)用“Never”傳播行為。
多事務(wù)語(yǔ)義配置時(shí),切入點(diǎn)一定不要疊加,否則將應(yīng)用兩次事務(wù)屬性,造成不必要的錯(cuò)誤及麻煩。
@Transactional實(shí)現(xiàn)事務(wù)管理
對(duì)聲明式事務(wù)管理,Spring提供基于@Transactional注解方式來(lái)實(shí)現(xiàn),但需要Java 5+。
注解方式是最簡(jiǎn)單的事務(wù)配置方式,可以直接在Java源代碼中聲明事務(wù)屬性,且對(duì)于每一個(gè)業(yè)務(wù)類(lèi)或方法如果需要事務(wù)都必須使用此注解。
接下來(lái)學(xué)習(xí)一下注解事務(wù)的使用吧:
1、定義業(yè)務(wù)邏輯實(shí)現(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命名空間定義,定義用于事務(wù)支持的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è)務(wù)實(shí)現(xiàn)配置,非常簡(jiǎn)單,使用以前定義的非侵入式業(yè)務(wù)實(shí)現(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、事務(wù)相關(guān)配置:
java代碼:
1
<tx:annotation-driven transaction-manager="txManager"/>
使用如上配置已支持聲明式事務(wù)。
3、修改測(cè)試方法并測(cè)試該配置方式是否好用:
將TransactionTest 類(lèi)的testServiceTransaction測(cè)試方法拷貝一份命名為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í)行測(cè)試,測(cè)試正常通過(guò),說(shuō)明該方式能正常工作,因?yàn)樵贏nnotationAddressServiceImpl類(lèi)的save方法中拋出異常,因此事務(wù)需要回滾,所以?xún)蓚€(gè)countAll操作都返回0。
9.4.7 @Transactional配置詳解
Spring提供的<tx:annotation-driven/>用于開(kāi)啟對(duì)注解事務(wù)管理的支持,從而能識(shí)別Bean類(lèi)上的@Transactional注解元數(shù)據(jù),其具有以下屬性:
transaction-manager:指定事務(wù)管理器名字,默認(rèn)為transactionManager,當(dāng)使用其他名字時(shí)需要明確指定;
proxy-target-class:表示將使用的代碼機(jī)制,默認(rèn)false表示使用JDK代理,如果為true將使用CGLIB代理
order:定義事務(wù)通知順序,默認(rèn)Ordered.LOWEST_PRECEDENCE,表示將順序決定權(quán)交給AOP來(lái)處理。
Spring使用@Transactional 來(lái)指定事務(wù)屬性,可以在接口、類(lèi)或方法上指定,如果類(lèi)和方法上都指定了@Transactional ,則方法上的事務(wù)屬性被優(yōu)先使用,具體屬性如下:
value:指定事務(wù)管理器名字,默認(rèn)使用<tx:annotation-driven/>指定的事務(wù)管理器,用于支持多事務(wù)管理器環(huán)境;
propagation:指定事務(wù)傳播行為,默認(rèn)為Required,使用Propagation.REQUIRED指定;
isolation:指定事務(wù)隔離級(jí)別,默認(rèn)為“DEFAULT”,使用Isolation.DEFAULT指定;
readOnly:指定事務(wù)是否只讀,默認(rèn)false表示事務(wù)非只讀;
timeout:指定事務(wù)超時(shí)時(shí)間,以秒為單位,默認(rèn)-1表示事務(wù)超時(shí)將依賴(lài)于底層事務(wù)系統(tǒng);
rollbackFor:指定一組異常類(lèi),遇到該類(lèi)異常將回滾事務(wù);
rollbackForClassname:指定一組異常類(lèi)名字,其含義與<tx:method>中的rollback-for屬性語(yǔ)義完全一樣;
noRollbackFor:指定一組異常類(lèi),即使遇到該類(lèi)異常也將提交事務(wù),即不回滾事務(wù);
noRollbackForClassname:指定一組異常類(lèi)名字,其含義與<tx:method>中的no-rollback-for屬性語(yǔ)義完全一樣;
Spring提供的@Transactional 注解事務(wù)管理內(nèi)部同樣利用環(huán)繞通知TransactionInterceptor實(shí)現(xiàn)事務(wù)的開(kāi)啟及關(guān)閉。
使用@Transactional注解事務(wù)管理需要特別注意以下幾點(diǎn):
如果在接口、實(shí)現(xiàn)類(lèi)或方法上都指定了@Transactional 注解,則優(yōu)先級(jí)順序?yàn)榉椒?gt;實(shí)現(xiàn)類(lèi)>接口;
建議只在實(shí)現(xiàn)類(lèi)或?qū)崿F(xiàn)類(lèi)的方法上使用@Transactional,而不要在接口上使用,這是因?yàn)槿绻褂肑DK代理機(jī)制是沒(méi)問(wèn)題,因?yàn)槠涫褂没诮涌诘拇?;而使用使用CGLIB代理機(jī)制時(shí)就會(huì)遇到問(wèn)題,因?yàn)槠涫褂没陬?lèi)的代理而不是接口,這是因?yàn)榻涌谏系腀Transactional注解是“不能繼承的”;
具體請(qǐng)參考基于JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理的實(shí)現(xiàn)Spring注解管理事務(wù)(@Trasactional)到底有什么區(qū)別。
在Spring代理機(jī)制下(不管是JDK動(dòng)態(tài)代理還是CGLIB代理),“自我調(diào)用”同樣不會(huì)應(yīng)用相應(yīng)的事務(wù)屬性,其語(yǔ)義和<tx:tags>中一樣;
默認(rèn)只對(duì)RuntimeException異常回滾;
在使用Spring代理時(shí),默認(rèn)只有在public可見(jiàn)度的方法的@Transactional 注解才是有效的,其它可見(jiàn)度(protected、private、包可見(jiàn))的方法上即使有@Transactional 注解也不會(huì)應(yīng)用這些事務(wù)屬性的,Spring也不會(huì)報(bào)錯(cuò),如果你非要使用非公共方法注解事務(wù)管理的話(huà),可考慮使用AspectJ。
微信公眾號(hào)【黃小斜】作者是螞蟻金服 JAVA 工程師,專(zhuān)注于 JAVA
后端技術(shù)棧:SpringBoot、SSM全家桶、MySQL、分布式、中間件、微服務(wù),同時(shí)也懂點(diǎn)投資理財(cái),堅(jiān)持學(xué)習(xí)和寫(xiě)作,相信終身學(xué)習(xí)的力量!關(guān)注公眾號(hào)后回復(fù)”架構(gòu)師“即可領(lǐng)取
Java基礎(chǔ)、進(jìn)階、項(xiàng)目和架構(gòu)師等免費(fèi)學(xué)習(xí)資料,更有數(shù)據(jù)庫(kù)、分布式、微服務(wù)等熱門(mén)技術(shù)學(xué)習(xí)視頻,內(nèi)容豐富,兼顧原理和實(shí)踐,另外也將贈(zèng)送作者原創(chuàng)的Java學(xué)習(xí)指南、Java程序員面試指南等干貨資源