概述
事務(wù)管理對(duì)于企業(yè)應(yīng)用來(lái)說(shuō)是至關(guān)重要的只冻,即使出現(xiàn)異常情況柄冲,它也可以保證數(shù)據(jù)的一致性放典。
Spring Framework對(duì)事務(wù)管理提供了一致的抽象烦感,其特點(diǎn)如下:
為不同的事務(wù)API提供一致的編程模型巡社,比如JTA(Java Transaction API), JDBC, Hibernate, JPA(Java Persistence API和JDO(Java Data Objects)
支持聲明式事務(wù)管理,特別是基于注解的聲明式事務(wù)管理手趣,簡(jiǎn)單易用
提供比其他事務(wù)API如JTA更簡(jiǎn)單的編程式事務(wù)管理API
與spring數(shù)據(jù)訪問(wèn)抽象的完美集成
事務(wù)管理方式
spring支持編程式事務(wù)管理和聲明式事務(wù)管理兩種方式晌该。
編程式事務(wù)管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。對(duì)于編程式事務(wù)管理绿渣,spring推薦使用TransactionTemplate朝群。
聲明式事務(wù)管理建立在AOP之上的。其本質(zhì)是對(duì)方法前后進(jìn)行攔截中符,然后在目標(biāo)方法開(kāi)始之前創(chuàng)建或者加入一個(gè)事務(wù)姜胖,在執(zhí)行完目標(biāo)方法之后根據(jù)執(zhí)行情況提交或者回滾事務(wù)。聲明式事務(wù)最大的優(yōu)點(diǎn)就是不需要通過(guò)編程的方式管理事務(wù)淀散,這樣就不需要在業(yè)務(wù)邏輯代碼中摻雜事務(wù)管理的代碼右莱,只需在配置文件中做相關(guān)的事務(wù)規(guī)則聲明(或通過(guò)基于@Transactional注解的方式)堵第,便可以將事務(wù)規(guī)則應(yīng)用到業(yè)務(wù)邏輯中。
顯然聲明式事務(wù)管理要優(yōu)于編程式事務(wù)管理隧出,這正是spring倡導(dǎo)的非侵入式的開(kāi)發(fā)方式。聲明式事務(wù)管理使業(yè)務(wù)代碼不受污染阀捅,一個(gè)普通的POJO對(duì)象胀瞪,只要加上注解就可以獲得完全的事務(wù)支持。和編程式事務(wù)相比饲鄙,聲明式事務(wù)唯一不足地方是凄诞,后者的最細(xì)粒度只能作用到方法級(jí)別,無(wú)法做到像編程式事務(wù)那樣可以作用到代碼塊級(jí)別忍级。但是即便有這樣的需求帆谍,也存在很多變通的方法,比如轴咱,可以將需要進(jìn)行事務(wù)管理的代碼塊獨(dú)立為方法等等汛蝙。
聲明式事務(wù)管理也有兩種常用的方式,一種是基于tx和aop名字空間的xml配置文件朴肺,另一種就是基于@Transactional注解窖剑。顯然基于注解的方式更簡(jiǎn)單易用,更清爽戈稿。
自動(dòng)提交(AutoCommit)與連接關(guān)閉時(shí)的是否自動(dòng)提交
自動(dòng)提交
默認(rèn)情況下西土,數(shù)據(jù)庫(kù)處于自動(dòng)提交模式。每一條語(yǔ)句處于一個(gè)單獨(dú)的事務(wù)中鞍盗,在這條語(yǔ)句執(zhí)行完畢時(shí)需了,如果執(zhí)行成功則隱式的提交事務(wù),如果
執(zhí)行失敗則隱式的回滾事務(wù)般甲。
對(duì)于正常的事務(wù)管理肋乍,是一組相關(guān)的操作處于一個(gè)事務(wù)之中,因此必須關(guān)閉數(shù)據(jù)庫(kù)的自動(dòng)提交模式敷存。不過(guò)住拭,這個(gè)我們不用擔(dān)心,spring會(huì)將底層連接的自動(dòng)提交特性設(shè)置為false历帚。
org/springframework/jdbc/datasource/DataSourceTransactionManager.java
// 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");
}
con.setautocommit(false);
}
有些數(shù)據(jù)連接池提供了關(guān)閉事務(wù)自動(dòng)提交的設(shè)置滔岳,最好在設(shè)置連接池時(shí)就將其關(guān)閉。但C3P0沒(méi)有提供這一特性挽牢,只能依靠spring來(lái)設(shè)置谱煤。
因?yàn)镴DBC規(guī)范規(guī)定,當(dāng)連接對(duì)象建立時(shí)應(yīng)該處于自動(dòng)提交模式禽拔,這是跨DBMS的缺省值刘离,如果需要,必須顯式的關(guān)閉自動(dòng)提交室叉。C3P0遵守這一規(guī)范,讓客戶代碼來(lái)顯式的設(shè)置需要的提交模式硫惕。
連接關(guān)閉時(shí)的是否自動(dòng)提交
當(dāng)一個(gè)連接關(guān)閉時(shí)茧痕,如果有未提交的事務(wù)應(yīng)該如何處理?JDBC規(guī)范沒(méi)有提及恼除,C3P0默認(rèn)的策略是回滾任何未提交的事務(wù)踪旷。這是一個(gè)正確的策略,但JDBC驅(qū)動(dòng)提供商之間對(duì)此問(wèn)題并沒(méi)有達(dá)成一致豁辉。
C3P0的autoCommitOnClose屬性默認(rèn)是false,沒(méi)有十分必要不要?jiǎng)铀钜啊;蛘呖梢燥@式的設(shè)置此屬性為false徽级,這樣會(huì)更明確气破。
基于注解的聲明式事務(wù)管理配置
spring-servlet.xml
<!-- transaction support-->
<!-- PlatformTransactionMnager -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- enable transaction annotation support -->
<tx:annotation-driven transaction-manager="txManager" />
還要在spring-servlet.xml中添加tx名字空間
...
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
...
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
...
MyBatis自動(dòng)參與到spring事務(wù)管理中,無(wú)需額外配置餐抢,只要org.mybatis.spring.SqlSessionFactoryBean引用的數(shù)據(jù)源與DataSourceTransactionManager引用的數(shù)據(jù)源一致即可现使,否則事務(wù)管理會(huì)不起作用。
另外需要下載依賴(lài)包aopalliance.jar放置到WEB-INF/lib目錄下旷痕。否則spring初始化時(shí)會(huì)報(bào)異常
java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor
spring事務(wù)特性
spring所有的事務(wù)管理策略類(lèi)都繼承自org.springframework.transaction.PlatformTransactionManager接口
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
其中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è)查詢,并且每次返回的記錄都相同召嘶。該級(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ù)用于客戶代碼只讀但不修改數(shù)據(jù)的情形栖疑,只讀事務(wù)用于特定情景下的優(yōu)化讨永,比如使用Hibernate的時(shí)候。
默認(rèn)為讀寫(xiě)事務(wù)遇革。
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的子類(lèi)(Errors也會(huì)導(dǎo)致事務(wù)回滾)揪漩,而拋出checked異常則不會(huì)導(dǎo)致事務(wù)回滾旋恼。
可以明確的配置在拋出那些異常時(shí)回滾事務(wù),包括checked異常奄容。也可以明確定義那些異常拋出時(shí)不回滾事務(wù)冰更。
還可以編程性的通過(guò)setRollbackOnly()方法來(lái)指示一個(gè)事務(wù)必須回滾,在調(diào)用完setRollbackOnly()后你所能執(zhí)行的唯一操作就是回滾昂勒。
@Transactional注解
@Transactional屬性
屬性 | 類(lèi)型 | 描述 |
---|---|---|
value | String | 可選的限定描述符蜀细,指定使用的事務(wù)管理器 |
propagation | enum: Propagation | 可選的事務(wù)傳播行為設(shè)置 |
isolation | enum: Isolation | 可選的事務(wù)隔離級(jí)別設(shè)置 |
readOnly | boolean | 讀寫(xiě)或只讀事務(wù),默認(rèn)讀寫(xiě) |
timeout | int (in seconds granularity) | 事務(wù)超時(shí)時(shí)間設(shè)置 |
rollbackFor | Class對(duì)象數(shù)組戈盈,必須繼承自Throwable | 導(dǎo)致事務(wù)回滾的異常類(lèi)數(shù)組 |
rollbackForClassName | 類(lèi)名數(shù)組奠衔,必須繼承自Throwable | 導(dǎo)致事務(wù)回滾的異常類(lèi)名字?jǐn)?shù)組 |
noRollbackFor | Class對(duì)象數(shù)組,必須繼承自Throwable | 不會(huì)導(dǎo)致事務(wù)回滾的異常類(lèi)數(shù)組 |
noRollbackForClassName | 類(lèi)名數(shù)組塘娶,必須繼承自Throwable | 不會(huì)導(dǎo)致事務(wù)回滾的異常類(lèi)名字?jǐn)?shù)組 |
用法
@Transactional 可以作用于接口涣觉、接口方法、類(lèi)以及類(lèi)方法上血柳。當(dāng)作用于類(lèi)上時(shí)官册,該類(lèi)的所有 public 方法將都具有該類(lèi)型的事務(wù)屬性,同時(shí)难捌,我們也可以在方法級(jí)別使用該標(biāo)注來(lái)覆蓋類(lèi)級(jí)別的定義膝宁。
雖然 @Transactional 注解可以作用于接口、接口方法根吁、類(lèi)以及類(lèi)方法上员淫,但是 Spring 建議不要在接口或者接口方法上使用該注解,因?yàn)檫@只有在使用基于接口的代理時(shí)它才會(huì)生效击敌。另外介返, @Transactional 注解應(yīng)該只被應(yīng)用到 public 方法上,這是由 Spring AOP 的本質(zhì)決定的。如果你在 protected圣蝎、private 或者默認(rèn)可見(jiàn)性的方法上使用 @Transactional 注解刃宵,這將被忽略,也不會(huì)拋出任何異常徘公。
默認(rèn)情況下牲证,只有來(lái)自外部的方法調(diào)用才會(huì)被AOP代理捕獲,也就是关面,類(lèi)內(nèi)部方法調(diào)用本類(lèi)內(nèi)部的其他方法并不會(huì)引起事務(wù)行為坦袍,即使被調(diào)用方法使用@Transactional注解進(jìn)行修飾。
@Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// these settings have precedence for this method
//方法上注解屬性會(huì)覆蓋類(lèi)注解上的相同屬性
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// do something
}
}
@Transactional 注解的屬性介紹
下面分別介紹一下 @Transactional 的幾個(gè)屬性等太。
value 和 transactionManager 屬性
它們兩個(gè)是一樣的意思捂齐。當(dāng)配置了多個(gè)事務(wù)管理器時(shí),可以使用該屬性指定選擇哪個(gè)事務(wù)管理器缩抡。
propagation 屬性
事務(wù)的傳播行為奠宜,默認(rèn)值為 Propagation.REQUIRED。
可選的值有:
Propagation.REQUIRED
如果當(dāng)前存在事務(wù)缝其,則加入該事務(wù)挎塌,如果當(dāng)前不存在事務(wù)徘六,則創(chuàng)建一個(gè)新的事務(wù)内边。
Propagation.SUPPORTS
如果當(dāng)前存在事務(wù),則加入該事務(wù)待锈;如果當(dāng)前不存在事務(wù)漠其,則以非事務(wù)的方式繼續(xù)運(yùn)行。
Propagation.MANDATORY
如果當(dāng)前存在事務(wù)竿音,則加入該事務(wù)和屎;如果當(dāng)前不存在事務(wù),則拋出異常春瞬。
Propagation.REQUIRES_NEW
重新創(chuàng)建一個(gè)新的事務(wù)柴信,如果當(dāng)前存在事務(wù),暫停當(dāng)前的事務(wù)宽气。
Propagation.NOT_SUPPORTED
以非事務(wù)的方式運(yùn)行随常,如果當(dāng)前存在事務(wù),暫停當(dāng)前的事務(wù)萄涯。
Propagation.NEVER
以非事務(wù)的方式運(yùn)行绪氛,如果當(dāng)前存在事務(wù),則拋出異常涝影。
Propagation.NESTED
和 Propagation.REQUIRED 效果一樣枣察。
這些概念理解起來(lái)實(shí)在是有點(diǎn)兒抽象,后文會(huì)用代碼示例解釋說(shuō)明。
isolation 屬性
事務(wù)的隔離級(jí)別序目,默認(rèn)值為 Isolation.DEFAULT臂痕。
可選的值有:
Isolation.DEFAULT
使用底層數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別。
Isolation.READ_UNCOMMITTED
Isolation.READ_COMMITTED
Isolation.REPEATABLE_READ
Isolation.SERIALIZABLE
timeout 屬性
事務(wù)的超時(shí)時(shí)間宛琅,默認(rèn)值為-1刻蟹。如果超過(guò)該時(shí)間限制但事務(wù)還沒(méi)有完成,則自動(dòng)回滾事務(wù)嘿辟。
readOnly 屬性
指定事務(wù)是否為只讀事務(wù)舆瘪,默認(rèn)值為 false;為了忽略那些不需要事務(wù)的方法红伦,比如讀取數(shù)據(jù)英古,可以設(shè)置 read-only 為 true。
rollbackFor 屬性
用于指定能夠觸發(fā)事務(wù)回滾的異常類(lèi)型昙读,可以指定多個(gè)異常類(lèi)型召调。
noRollbackFor 屬性
拋出指定的異常類(lèi)型,不回滾事務(wù)蛮浑,也可以指定多個(gè)異常類(lèi)型
https://www.ibm.com/developerworks/cn/java/j-master-spring-transactional-use/index.html
https://www.cnblogs.com/yepei/p/4716112.html