spring事務(wù)隔離級別、傳播機制以及簡單配置方式
1. 編程式事務(wù) 當(dāng)系統(tǒng)需要明確的,細(xì)粒度的控制各個事務(wù)的邊界,應(yīng)選擇編程式事務(wù)。
2. 聲明式事務(wù) 當(dāng)系統(tǒng)對于事務(wù)的控制粒度較粗時,應(yīng)該選擇申明式事務(wù),通過<tx>標(biāo)簽和<aop>切面形式在xml中進行配置。
3. 無論你選擇上述何種事務(wù)方式去實現(xiàn)事務(wù)控制,spring都提供基于門面設(shè)計模式的事務(wù)管理器供選擇,如下是spring事務(wù)中支持的事務(wù)管理器
事務(wù)管理器實現(xiàn)(org.springframework.*)
使用時機
jdbc.datasource.DataSourceTransactionManager
使用jdbc的抽象以及ibatis支持
orm.hibernate.HibernateTransactionManager
使用hibernate支持(默認(rèn)3.0以下版本)
orm.hibernate3.HibernateTransactionManager
使用hibernate3支持
transaction.jta.JtaTransactionManager
使用分布式事務(wù)(分布式數(shù)據(jù)庫支持)
orm.jpa.JpaTransactionManager
使用jpa做為持久化工具
orm.toplink.TopLinkTransactionManager
使用TopLink持久化工具
orm.jdo.JdoTransactionManager
使用Jdo持久化工具
jms.connection.JmsTransactionManager
使用JMS 1.1+
jms.connection.JmsTransactionManager102
使用JMS 1.0.2
transaction.jta.OC4JJtaTransactionManager
使用oracle的OC4J JEE容器
transaction.jta.WebLogicJtaTransactionManager
在weblogic中使用分布式數(shù)據(jù)庫
jca.cci.connection.CciLocalTransactionManager
使用jrping對J2EE Connector Architecture (JCA)和Common Client Interface (CCI)的支持
配置示例如下:
<bean id='transactionManager'class='org.springframework.jdbc.datasource.DataSourceTransactionManager'> <propertyname='dataSource' ref='dataSource'/> //引入數(shù)據(jù)源</bean>二、spring支持7種事務(wù)傳播行為
傳播行為
含義
propagation_required(xml文件中為required)
表示當(dāng)前方法必須在一個具有事務(wù)的上下文中運行,如有客戶端有事務(wù)在進行,那么被調(diào)用端將在該事務(wù)中運行,否則的話重新開啟一個事務(wù)。(如果被調(diào)用端發(fā)生異常,那么調(diào)用端和被調(diào)用端事務(wù)都將回滾)
propagation_supports(xml文件中為supports)
表示當(dāng)前方法不必需要具有一個事務(wù)上下文,但是如果有一個事務(wù)的話,它也可以在這個事務(wù)中運行
propagation_mandatory(xml文件中為mandatory)
表示當(dāng)前方法必須在一個事務(wù)中運行,如果沒有事務(wù),將拋出異常
propagation_nested(xml文件中為nested)
表示如果當(dāng)前方法正有一個事務(wù)在運行中,則該方法應(yīng)該運行在一個嵌套事務(wù)中,被嵌套的事務(wù)可以獨立于被封裝的事務(wù)中進行提交或者回滾。如果封裝事務(wù)存在,并且外層事務(wù)拋出異常回滾,那么內(nèi)層事務(wù)必須回滾,反之,內(nèi)層事務(wù)并不影響外層事務(wù)。如果封裝事務(wù)不存在,則同propagation_required的一樣
propagation_never(xml文件中為never)
表示當(dāng)方法務(wù)不應(yīng)該在一個事務(wù)中運行,如果存在一個事務(wù),則拋出異常
propagation_requires_new(xml文件中為requires_new)
表示當(dāng)前方法必須運行在它自己的事務(wù)中。一個新的事務(wù)將啟動,而且如果有一個現(xiàn)有的事務(wù)在運行的話,則這個方法將在運行期被掛起,直到新的事務(wù)提交或者回滾才恢復(fù)執(zhí)行。
propagation_not_supported(xml文件中為not_supported)
表示該方法不應(yīng)該在一個事務(wù)中運行。如果有一個事務(wù)正在運行,他將在運行期被掛起,直到這個事務(wù)提交或者回滾才恢復(fù)執(zhí)行
三、spring中的事務(wù)隔離級別隔離級別
含義
isolation_default
使用數(shù)據(jù)庫默認(rèn)的事務(wù)隔離級別
isolation_read_uncommitted
允許讀取尚未提交的修改,可能導(dǎo)致臟讀、幻讀和不可重復(fù)讀
isolation_read_committed
允許從已經(jīng)提交的事務(wù)讀取,可防止臟讀、但幻讀,不可重復(fù)讀仍然有可能發(fā)生
isolation_repeatable_read
對相同字段的多次讀取的結(jié)果是一致的,除非數(shù)據(jù)被當(dāng)前事務(wù)自生修改。可防止臟讀和不可重復(fù)讀,但幻讀仍有可能發(fā)生
isolation_serializable
完全服從acid隔離原則,確保不發(fā)生臟讀、不可重復(fù)讀、和幻讀,但執(zhí)行效率最低。
臟讀、不可重復(fù)讀、幻象讀概念說明:
a.臟讀:指當(dāng)一個事務(wù)正字訪問數(shù)據(jù),并且對數(shù)據(jù)進行了修改,而這種數(shù)據(jù)還沒有提交到數(shù)據(jù)庫中,這時,另外一個事務(wù)也訪問這個數(shù)據(jù),然后使用了這個數(shù)據(jù)。因為這個數(shù)據(jù)還沒有提交那么另外一個事務(wù)讀取到的這個數(shù)據(jù)我們稱之為臟數(shù)據(jù)。依據(jù)臟數(shù)據(jù)所做的操作肯能是不正確的。
b.不可重復(fù)讀:指在一個事務(wù)內(nèi),多次讀同一數(shù)據(jù)。在這個事務(wù)還沒有執(zhí)行結(jié)束,另外一個事務(wù)也訪問該同一數(shù)據(jù),那么在第一個事務(wù)中的兩次讀取數(shù)據(jù)之間,由于第二個事務(wù)的修改第一個事務(wù)兩次讀到的數(shù)據(jù)可能是不一樣的,這樣就發(fā)生了在一個事物內(nèi)兩次連續(xù)讀到的數(shù)據(jù)是不一樣的,這種情況被稱為是不可重復(fù)讀。
c.幻象讀:一個事務(wù)先后讀取一個范圍的記錄,但兩次讀取的紀(jì)錄數(shù)不同,我們稱之為幻象讀(兩次執(zhí)行同一條 select 語句會出現(xiàn)不同的結(jié)果,第二次讀會增加一數(shù)據(jù)行,并沒有說這兩次執(zhí)行是在同一個事務(wù)中)
四、spring事務(wù)只讀屬性spring事務(wù)只讀的含義是指,如果后端數(shù)據(jù)庫發(fā)現(xiàn)當(dāng)前事務(wù)為只讀事務(wù),那么就會進行一系列的優(yōu)化措施。它是在后端數(shù)據(jù)庫進行實施的,因此,只有對于那些有可能啟動一個新事務(wù)的傳播行為(REQUIRED,REQUIRES_NEW,NESTED)的方法來說,才有意義。(測試表明,當(dāng)使用JDBC事務(wù)管理器并設(shè)置當(dāng)前事務(wù)為只讀時,并不能發(fā)生預(yù)期的效果,即能執(zhí)行刪除,更新,插入操作)
五、spring的事務(wù)超時有的時候為了系統(tǒng)中關(guān)鍵部分的性能問題,它的事務(wù)執(zhí)行時間應(yīng)該盡可能的短。因此可以給這些事務(wù)設(shè)置超時時間,以秒為單位。我們知道事務(wù)的開始往往都會發(fā)生數(shù)據(jù)庫的表鎖或者被數(shù)據(jù)庫優(yōu)化為行鎖,如果允許時間過長,那么這些數(shù)據(jù)會一直被鎖定,影響系統(tǒng)的并發(fā)性。
因為超時時鐘是在事務(wù)開始的時候啟動,因此只有對于那些有可能啟動新事物的傳播行為(REQUIRED,REQUIRES_NEW,NESTED)的方法來說,事務(wù)超時才有意義。
六、事務(wù)回滾規(guī)則spring中可以指定當(dāng)方法執(zhí)行并拋出異常的時候,哪些異常回滾事務(wù),哪些異常不回滾事務(wù)。默認(rèn)情況下,只在方法拋出運行時異常的時候才回滾(runtime exception)。而在出現(xiàn)受阻異常(checked exception)時不回滾事務(wù)。當(dāng)然可以采用申明的方式指定哪些受阻異常像運行時異常那樣指定事務(wù)回滾。
七、單個方法的事務(wù)傳播機制(注意這里說的是單個方法)1、整備工作: spring申明式事務(wù)配置,將aop,tx命名空間添加到當(dāng)前的spring配置文件頭中,這里不在累述
2、定義一個事務(wù)AOP通知(add開頭名單方法)
<tx:advice transactionManager='transactionManager'> <tx:attributes> <tx:methodname='add*' propagation='REQUIRED'/> </tx:attributes> </tx:advice>
3、定義一個事務(wù)切面,即應(yīng)該在哪些類的哪些方法上面進行事務(wù)切入,多個包體可以使用||隔開
<aop:config> <aop:advisor pointcut='execution(* *..zx.spring.UserService*.*(..))||execution(**..spring.ServiceFacade.*(..))||execution(**..spring.BookService.*(..))' advice-ref='txAdvice'/> </aop:config>
4、事務(wù)通知中Propagation設(shè)置為7中傳播行為的情況如下(單方法):
@1、方法的事務(wù)傳播機制為REQUIRED, REQUIRES_NEW,并且拋出運行時異常時,將回滾事務(wù)。當(dāng)方法拋出受檢查的異常時,將不會回滾事務(wù)。
@2、方法的事務(wù)傳播機制為MANDATORY,由于單個方法執(zhí)行沒有指定任何事務(wù)傳播機制,因此拋出異常。
@3、方法的事務(wù)傳播機制為NESTED,由于NESTED內(nèi)嵌事務(wù),如果沒有外層事務(wù),則新建一個事務(wù),行為同REQUIRED一樣。
@4、方法的事務(wù)傳播機制為NEVER,由于發(fā)生運行時異常,事務(wù)本應(yīng)該回滾,但是事務(wù)傳播機制為NEVER,因此找不到事務(wù)進行回滾。
@5、方法的事務(wù)傳播機制為NOT_SUPPORTED,單個方法被執(zhí)行時,不會創(chuàng)建事務(wù)。如果當(dāng)前有事務(wù),將封裝事務(wù)掛起,知道該方法執(zhí)行完成再恢復(fù)封裝事務(wù),繼續(xù)執(zhí)行。
@6、方法的事務(wù)傳播機制為SUPPORTS,單個方法 調(diào)用時supports行為同NEVER一樣,不會創(chuàng)建事務(wù),只是如果有事務(wù),則會加入到當(dāng)前事務(wù)中去。
八、多個方法間的事務(wù)傳播機制(注意這里說的是多個方法)1、整備工作: spring申明式事務(wù)配置,將aop,tx命名空間添加到當(dāng)前的spring配置文件頭中,不在累述。
2、定義一個事務(wù)AOP通知(add開頭名單方法)
<tx:advice transactionManager='transactionManager'> <tx:attributes> <tx:methodname='add*' propagation='REQUIRED'/><tx:methodname='update*' propagation='REQUIRED'/> </tx:attributes> </tx:advice>
3、定義一個事務(wù)切面,即應(yīng)該在哪些類的哪些方法上面進行事務(wù)切入
<aop:config> <aop:advisor pointcut='execution(**..zx.spring.UserService*.*(..))||execution(**..spring.ServiceFacade.*(..))||execution(**..spring.BookService.*(..))' advice-ref='txAdvice'/> </aop:config>
4、事務(wù)通知中Propagation設(shè)置為7中傳播行為的情況如下(多方法間傳遞):
@1、多個方法的事務(wù)傳播機制都為REQUIRED,由于REQUIRED方式是如果有一個事務(wù),則加入事務(wù)中,如果沒有,則新建一個事務(wù),也就是說方法1一開始就會創(chuàng)建一個事務(wù),而方法2發(fā)現(xiàn)有事務(wù)存在則會加入到事務(wù)中,方法2拋出運行時異常,整個事務(wù)將會回滾。如果方法2拋出的是可捕獲異常,事務(wù)將不起作用,方法1執(zhí)行成功插入,方法2異常中斷。或者我們將方法2設(shè)置rollback-for屬性,那么在指定的異常之內(nèi)事務(wù)都將回滾
@2、方法1的事務(wù)傳播機制為REQUIRED,方法2的事務(wù)傳播機制為NEVER,由于方法1會創(chuàng)建一個事務(wù),而方法2是NEVER,不應(yīng)該在事務(wù)中運行,因此導(dǎo)致了沖突會報錯。Existingtransaction found for transaction marked with propagation ’never’
@3、方法1的事務(wù)傳播機制為REQUIRED,方法2的事務(wù)傳播機制為MANDATORY,執(zhí)行機制和@1一致。
@4、方法1的事務(wù)傳播機制為REQUIRED,方法2的事務(wù)傳播機制為NESTED,內(nèi)嵌事務(wù),也就是方法1執(zhí)行體內(nèi)放入方法2的調(diào)用,由于方法2的事務(wù)傳播機制為NESTED內(nèi)嵌事務(wù),因此,開始一個新的事務(wù)。并且方法2內(nèi)部拋出RuntimeException,因此內(nèi)部嵌套事務(wù)回滾到外層事務(wù)創(chuàng)建的保存點。
注意這個地方,我們拋出的是運行時異常,如果我們拋出受檢查的異常,那么spring會默認(rèn)的忽略此異常。
如果內(nèi)層事務(wù)拋出檢查異常,那么外層事務(wù)將忽略此異常,但是會產(chǎn)生一個問題。那就是:外層事務(wù)使用jdbc的保存點API來實現(xiàn)嵌套事務(wù),
但是數(shù)據(jù)庫不一定支持。我做測試的是oracle數(shù)據(jù)庫,jdbc事務(wù)管理器在內(nèi)層事務(wù)拋出檢查異常后,將會在內(nèi)層事務(wù)結(jié)束后,釋放外層事務(wù)創(chuàng)建的保存點,這個時候數(shù)據(jù)庫不一定支持。因此可能會拋出如下異常java.sql.SQLException:不支持的特性。
總結(jié): 對于NESTED內(nèi)層事務(wù)而言,內(nèi)層事務(wù)獨立于外層事務(wù),可以獨立遞交或者回滾,如果內(nèi)層事務(wù)拋出的是運行異常,外層事務(wù)進行回滾,內(nèi)層事務(wù)也會進行回滾。
@5、方法1的事務(wù)傳播機制為REQUIRED,方法2的事務(wù)傳播機制為REQUIRES_NEW。(情況比較復(fù)雜)
情況一,方法2不拋出任何異常下:方法1先創(chuàng)建事務(wù),但是方法2也要運行在自己的事務(wù)中,因此也會創(chuàng)建一個事務(wù),方法1的事務(wù)將會掛起,直到方法2的事務(wù)執(zhí)行完成才會接著事務(wù)的提交完成
情況二,方法2拋出運行時異常下: 對于REQUIRES_NEW事務(wù)傳播機制,如果被調(diào)用端(方法2)拋出運行時異常,則被調(diào)用端事務(wù)回滾,如果調(diào)用段代碼(方法1)捕獲了被調(diào)用端拋出的運行時異常,那么調(diào)用端事務(wù)提交,不回滾。反之,如果調(diào)用段代碼未捕獲被調(diào)用端拋出的運行時異常,那么調(diào)用端事務(wù)回滾,不提交。
@6、方法1的事務(wù)傳播機制為REQUIRED,方法2的事務(wù)傳播機制為SUPPORTS,方法1創(chuàng)建一個事務(wù),方法2加入到當(dāng)前事務(wù)中,方法2拋出運行異常將進行事務(wù)的回滾,如果方法1不具有事務(wù),由于方法2的特性是事務(wù)環(huán)境可有可無,方法2也不會運行在事務(wù)中,所以也不會進行回滾。如果方法2拋出的是受檢查異常,異常將會忽略,事務(wù)將會提交,當(dāng)然還有別的情況沒有一一驗證
@7、方法1的事務(wù)傳播機制為REQUIRED,方法2的事務(wù)傳播機制為NOT_SUPPORTED,由于方法2事務(wù)特性,方法1的事務(wù)將會被掛起,方法2拋出運行時異常,但是方法2是不在事務(wù)環(huán)境中運行的,所以回滾不了,而方法1中未捕獲此異常,將進行事務(wù)回滾,如果方法2拋出的是運行異常,即使方法1沒有捕獲此異常,也將被忽略,不進行事務(wù)的回滾。
PS:以上都是對粗粒度的方法的事務(wù)操作,都是在數(shù)據(jù)無問題,特意拋出異常的情況下來判斷事務(wù)的特性,并在什么情況下事務(wù)將會起作用。而且spring的事務(wù),在服務(wù)類內(nèi)部的方法間調(diào)用,有可能存在被調(diào)用方法配置的事務(wù)不起作用,調(diào)用方在加上事務(wù)就起作用了。還值得測試深究
九、spring聲明式事務(wù)配置的幾種方式1、基于@Transactional注解(推薦配置)
<!-- 事務(wù)管理器,對mybatis操作數(shù)據(jù)庫進行事務(wù)控制,spring使用jdbc的事務(wù)控制類 --><bean class='org.springframework.jdbc.datasource.DataSourceTransactionManager'><property name='dataSource' ref='dataSourceSuport'></property></bean>
<!—開啟spring 事務(wù)注解支持 mode='aspectj'表示采用切面 mode='proxy'表示代理模式(默認(rèn)) --> <tx:annotation-driven transaction-manager='transactionManager' />
MyBatis自動參與到spring事務(wù)管理中,無需額外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的數(shù)據(jù)源與DataSourceTransactionManager引用的數(shù)據(jù)源一致即可,否則事務(wù)管理會不起作用。
@Transactional可以作用于接口、接口方法、類以及類方法上。當(dāng)作用于類上時,該類的所有 public 方法將都具有該類型的事務(wù)屬性,同時,我們也可以在方法級別使用該標(biāo)注來覆蓋類級別的定義。 雖然@Transactional 注解可以作用于接口、接口方法、類以及類方法上,但是 Spring 建議不要在接口或者接口方法上使用該注解,因為這只有在使用基于接口的代理時它才會生效。另外,@Transactional 注解應(yīng)該只被應(yīng)用到public 方法上,這是由 Spring AOP 的本質(zhì)決定的。如果你在 protected、private 或者默認(rèn)可見性的方法上使用@Transactional 注解,這將被忽略,也不會拋出任何異常。默認(rèn)情況下,只有來自外部的方法調(diào)用才會被AOP代理捕獲,也就是,類內(nèi)部方法調(diào)用本類內(nèi)部的其他方法并不會引起事務(wù)行為,即使被調(diào)用方法使用@Transactional注解進行修飾。
2、基于<tx>與<aop>切面的方式進行事務(wù)配置
<!-- 事務(wù)管理器,對mybatis操作數(shù)據(jù)庫進行事務(wù)控制,spring使用jdbc的事務(wù)控制類 --> <bean class='org.springframework.jdbc.datasource.DataSourceTransactionManager'> <property name='dataSource' ref='dataSourceSuport'></property> </bean> <!-- spring 事務(wù)注解 mode='aspectj'表示采用切面 mode='proxy'表示代理模式(默認(rèn)) --> <tx:annotation-driven transaction-manager='transactionManager' /> <!-- 通知 映射到上面的事務(wù)管理器--> <tx:advice id='txAdive'transaction-manager='transactionManager'> <tx:attributes> <!--name什么方法名稱開頭 傳播行為 REQUIRED必須事務(wù) rollback-for什么異常類進行回滾,逗號隔開 --> <tx:method name='save*' propagation='REQUIRED'rollback-for='java.lang.DaoException'/> <tx:method name='delete*' propagation='REQUIRED'rollback-for='java.lang.DaoException'/> <tx:method name='insert*' propagation='REQUIRED'rollback-for='java.lang.DaoException'/> <tx:method name='update*' propagation='REQUIRED'rollback-for='java.lang.DaoException'/> <!-- 不是必須的,而且查詢類型為事務(wù)只讀的可以提高數(shù)據(jù)庫性能優(yōu)化 --> <tx:method name='find*' propagation='SUPPORTS'read-only='true'/> <tx:method name='get*' propagation='SUPPORTS'read-only='true'/> <tx:method name='select*' propagation='SUPPORTS'read-only='true'/> </tx:attributes> </tx:advice> <!-- aop切面并配置切入點入進行事物管理 指向上面的映射 --> <aop:config> <aop:pointcut expression='execution(* com.zht.service.serviceImpl.*.*(..))' /> <aop:advisor advice-ref='txAdive' pointcut-ref='txPointcut'/> </aop:config> <aop:aspectj-autoproxy proxy-target- />
3、基于TransactionProxyFactoryBean (bean工廠代理)--需要顯示的為每一個業(yè)務(wù)類配置,每多一個業(yè)務(wù)類就要進行添加,這里不在描述。
4、基于TransactionInterceptor(事務(wù)攔截器)的方式進行配置。
<bean id='transactionManager'class='org.springframework.orm.hibernate3.HibernateTransactionManager'><propertyname='sessionFactory'><ref bean='sessionFactory'/></property></bean><!--事務(wù)攔截器,激活事務(wù)管理器所必須的bean --><bean id='transactionInterceptor'class='org.springframework.transaction.interceptor.TransactionInterceptor'><property name='transactionManager'><ref bean='transactionManager' /></property><!--配置事務(wù)屬性 --><property name='transactionAttributes'><props><prop key='delete*'>PROPAGATION_REQUIRED</prop><prop key='add*'>PROPAGATION_REQUIRED</prop><prop key='update*'>PROPAGATION_REQUIRED</prop><prop key='save*'>PROPAGATION_REQUIRED</prop><prop key='find*'>PROPAGATION_REQUIRED,readOnly</prop></props></property></bean><!--定義事務(wù)處理代理bean,他需要兩個屬性,一個是指定需要代理的bean,另一個是代理bean所需的事務(wù)攔截器 --><bean class='org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator'><property name='beanNames'><list><value>tempService</value></list></property><propertyname='interceptorNames'><list><value>transactionInterceptor</value></list></property></bean><!--業(yè)務(wù)邏輯層 --><bean class='com.cj.transaction.service.TempService'abstract='false' lazy-init='default'autowire='default' dependency-check='default'><property name='userDAO'><ref bean='userDAO' /></property><property name='deptDAO'><ref bean='deptDAO' /></property></bean><bean class='com.cj.transaction.hibernate.UserDAO'><property name='sessionFactory'><ref bean='sessionFactory'/></property></bean><bean id='deptDAO'class='com.cj.transaction.hibernate.DeptDAO'><property name='sessionFactory'><ref bean='sessionFactory'/></property></bean>
如果模塊過多話,可以考慮根據(jù)bean的名稱用自動創(chuàng)建事務(wù)代理的方式
<!--自動代理 --><bean id='autoproxy'class='org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator'><property name='beanNames'><list><value>*Service</value></list></property><property name='interceptorNames'><list><value>transactionInterceptor</value></list></property></bean>
PS:以上為自己收集以及在spring+springMVC+mybatis框架測試總結(jié)的一些內(nèi)容,特記錄下,如果有理解錯的地方望拍磚指出,感激不盡。希望能給大家一個參考,也希望大家多多支持好吧啦網(wǎng)。。
相關(guān)文章:
1. 如何用python開發(fā)Zeroc Ice應(yīng)用2. ASP錯誤捕獲的幾種常規(guī)處理方式3. python用pyecharts實現(xiàn)地圖數(shù)據(jù)可視化4. python基于opencv批量生成驗證碼的示例5. python軟件測試Jmeter性能測試JDBC Request(結(jié)合數(shù)據(jù)庫)的使用詳解6. npm下載慢或下載失敗問題解決的三種方法7. ASP編碼必備的8條原則8. Python查找算法之折半查找算法的實現(xiàn)9. python numpy.power()數(shù)組元素求n次方案例10. 使用Spry輕松將XML數(shù)據(jù)顯示到HTML頁的方法
