原貼:java
http://blog.csdn.net/zhuxinquan61/article/details/71075051spring
spring的事務管理,通常有四種方式:sql
1. 編程式事務管理實現:數據庫
transactionTemplate.execute(){}express
2. 基於TransactionProxyFactoryBean的聲明式事務管理:編程
bean配置代理app
3. 基於AspectJ的XML聲明式事務管理:框架
配置通知(tx advice)、切面、切入點。ide
可實現全部execution表達式下的包事務測試
4. 基於註解的聲明式事務管理:
@Transactional(propagation = Propagation.REQUIRED)
<tx:annotation-driven transaction-manager="transactionManager"/>(開啓註解事務)
-------------- 分割線 -----------------
事務是邏輯上的一組操做,這組操做要麼所有成功,要麼所有失敗,最爲典型的就是銀行轉帳的案例:
A要向B轉帳,如今A,B各自帳戶中有1000元,A要給B轉200元,那麼這個轉帳就必須保證是一個事務,防止中途由於各類緣由致使A帳戶資金減小而B帳戶資金未添加,或者B帳戶資金添加而A帳戶資金未減小,這樣不是用戶有損失就是銀行有損失,爲了保證轉帳先後的一致性就必須保證轉帳操做是一個事務。
事務具備的ACID特性:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)
首先,這篇文章先說起一些Spring中事務有關的API,而後分別實現編程式事務管理和聲明式事務管理,其中聲明式事務管理分別使用基於TransactionProxyFactoryBean
的方式、基於AspectJ的XML方式、基於註解方式進行實現。
首先,咱們簡單看一下Spring事務管理須要說起的接口,Spring事務管理高層抽象主要包括3個接口
PlatformTransactionManager
:事務管理器(用來管理事務,包含事務的提交,回滾)
TransactionDefinition
:事務定義信息(隔離,傳播,超時,只讀)
TransactionStatus
:事務具體運行狀態
Spring根據事務定義信息(TransactionDefinition)由平臺事務管理器(PlatformTransactionManager)真正進行事務的管理,在進行事務管理的過程當中,事務會產生運行狀態,狀態保存在TransactionStatus中
PlatformTransactionManager:
Spring爲不一樣的持久化框架提供了不一樣的PlatformTransactionManager如:
在使用Spring JDBC或iBatis進行持久化數據時,採用DataSourceTransactionManager
在使用Hibernate進行持久化數據時使用HibernateTransactionManager
TransactionDefinition:
TransactionDefinition
接口中定義了一組常量,包括事務的隔離級別,事務的傳播行爲,超時信息,其中還定義了一些方法,可得到事務的隔離級別,超時信息,是否只讀。
傳播行爲主要解決業務層方法之間的相互調用產生的事務應該如何傳遞的問題。
TransactionDefinition
中定義的屬性常量以下:
Field(屬性) | Description(描述) |
---|---|
ISOLATION_DEFAULT | 使用底層數據存儲的默認隔離級別 |
ISOLATION_READ_COMMITTED | 表示防止髒讀;可能會發生不可重複的讀取和幻像讀取 |
ISOLATION_READ_UNCOMMITTED | 表示可能會發生髒讀,不可重複的讀取和幻像讀取 |
ISOLATION_REPEATABLE_READ | 表示禁止髒讀和不可重複讀;能夠發生幻影讀取 |
ISOLATION_SERIALIZABLE | 表示能夠防止髒讀,不可重複的讀取和幻像讀取 |
PROPAGATION_MANDATORY | 支持當前交易;若是不存在當前事務,則拋出異常 |
PROPAGATION_NESTED | 若是當前事務存在,則在嵌套事務中執行,其行爲相似於PROPAGATION_REQUIRED |
PROPAGATION_NEVER | 不支持當前交易;若是當前事務存在,則拋出異常 |
PROPAGATION_NOT_SUPPORTED | 不支持當前交易;而是老是非事務地執行 |
PROPAGATION_REQUIRED | 支持當前交易;若是不存在,建立一個新的 |
PROPAGATION_REQUIRES_NEW | 建立一個新的事務,掛起當前事務(若是存在) |
PROPAGATION_SUPPORTS | 支持當前交易;若是不存在,則執行非事務性的 |
TIMEOUT_DEFAULT | 使用底層事務系統的默認超時,若是不支持超時,則爲none |
TransationStatus:
在該接口中提供了一些方法:
Method | Description |
---|---|
flush() | 將基礎會話刷新到數據存儲(若是適用):例如,全部受影響的Hibernate / JPA會話 |
hasSavepoint() | 返回此事務是否內部攜帶保存點,也就是基於保存點建立爲嵌套事務 |
isCompleted() | 返回此事務是否完成,便是否已經提交或回滾 |
isNewTransaction() | 返回當前交易是不是新的(不然首先參與現有交易,或者潛在地不會在實際交易中運行) |
isRollbackOnly() | 返回事務是否已被標記爲僅回滾(由應用程序或由事務基礎結構) |
setRollbackOnly() | 設置事務回滾 |
瞭解了上述接口,接下來咱們經過轉帳案例來實現Spring的事務管理:
數據庫中account表以下:
AccountDao.java:
package com.spring.demo1; /** * Created by zhuxinquan on 17-4-27. */ public interface AccountDao { public void outMoney(String out, Double money); public void inMoney(String in, Double money); }
AccountDaoImp.java
package com.spring.demo1; import org.springframework.jdbc.core.support.JdbcDaoSupport; /** * Created by zhuxinquan on 17-4-27. */ public class AccountDaoImp extends JdbcDaoSupport implements AccountDao { public void outMoney(String out, Double money) { String sql = "update account set money = money - ? where name = ?"; this.getJdbcTemplate().update(sql, money, out); } public void inMoney(String in, Double money) { String sql = "update account set money = money + ? where name = ?"; this.getJdbcTemplate().update(sql, money, in); } }
AccountService.java
package com.spring.demo1; /** * Created by zhuxinquan on 17-4-27. */ public interface AccountService { public void transfer(String out, String in, Double money); }
AccountServiceImp.java
package com.spring.demo1; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; /** * Created by zhuxinquan on 17-4-27. */ public class AccountServiceImp implements AccountService{ private AccountDao accountDao; // 注入事務管理的模板 private TransactionTemplate transactionTemplate; public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } public void transfer(final String out, final String in, final Double money) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { accountDao.outMoney(out, money); //此處除0模擬轉帳發生異常 int i = 1 / 0; accountDao.inMoney(in, money); } }); } }
建立Spring配置文件applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="classpath:jdbc.properties"/> <!--配置c3p0鏈接池--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.Driver}"/> <property name="jdbcUrl" value="${jdbc.URL}"/> <property name="user" value="${jdbc.USERNAME}"/> <property name="password" value="${jdbc.PASSWD}"/> </bean> <!--配置業務層類--> <bean id="accountService" class="com.spring.demo1.AccountServiceImp"> <property name="accountDao" ref="accountDao"/> <property name="transactionTemplate" ref="transactionTemplate"/> </bean> <!--配置Dao的類--> <bean id="accountDao" class="com.spring.demo1.AccountDaoImp"> <property name="dataSource" ref="dataSource"/> </bean> <!--配置事務管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--配置事務管理模板,Spring爲了簡化事務管理的代碼而提供的類--> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"/> </bean> </beans>
編寫測試類以下:
SpringDemoTest1.java
import com.spring.demo1.AccountService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.annotation.Resource; /** * Created by zhuxinquan on 17-4-27. * Spring編程式事務管理 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringDemoTest1 { @Resource(name = "accountService") private AccountService accountService; @Test public void demo1(){ accountService.transfer("aaa", "bbb", 200d); } }
Dao與Service代碼與1中相同,applicationContext2.xml以下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="classpath:jdbc.properties"/> <!--配置c3p0鏈接池--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.Driver}"/> <property name="jdbcUrl" value="${jdbc.URL}"/> <property name="user" value="${jdbc.USERNAME}"/> <property name="password" value="${jdbc.PASSWD}"/> </bean> <!--配置業務層類--> <bean id="accountService" class="com.spring.demo2.AccountServiceImp"> <property name="accountDao" ref="accountDao"/> </bean> <!--配置Dao的類--> <bean id="accountDao" class="com.spring.demo2.AccountDaoImp"> <property name="dataSource" ref="dataSource"/> </bean> <!--配置事務管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--配置業務層代理--> <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!--配置目標對象--> <property name="target" ref="accountService"/> <!--注入事務管理器--> <property name="transactionManager" ref="transactionManager"/> <!--注入事務的屬性--> <property name="transactionAttributes"> <props> <!-- prop格式 * PROPAGATION :事務的傳播行爲 * ISOLATION :事務的隔離級別 * readOnly :只讀(不能夠進行修改,插入,刪除的操做) * -Exception :發生哪些異常回滾事務 * +Exception :發生哪些異常不回滾事務 --> <prop key="transfer">PROPAGATION_REQUIRED</prop> <!--<prop key="transfer">PROPAGATION_REQUIRED,readOnly</prop>--> <!--<prop key="transfer">PROPAGATION_REQUIRED, +java.lang.ArithmeticException</prop>--> </props> </property> </bean> </beans>
此時注入時須要選擇代理類,由於在代理類中進行加強操做,測試代碼以下:
SpringDemoTest2.java
import com.spring.demo2.AccountService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.annotation.Resource; /** * Created by zhuxinquan on 17-4-27. * Spring聲明式事務管理:基於TransactionProxyFactoryBean的方式 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext2.xml") public class SpringDemoTest2 { /* 此時須要注入代理類:由於代理類進行加強操做 */ // @Resource(name = "accountService") @Resource(name = "accountServiceProxy") private AccountService accountService; @Test public void demo1(){ accountService.transfer("aaa", "bbb", 200d); } }
在這種方式下Dao和Service的代碼也沒有改變,applicationContext3.xml以下:
applicationContext3.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd"> <context:property-placeholder location="classpath:jdbc.properties"/> <!--配置c3p0鏈接池--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.Driver}"/> <property name="jdbcUrl" value="${jdbc.URL}"/> <property name="user" value="${jdbc.USERNAME}"/> <property name="password" value="${jdbc.PASSWD}"/> </bean> <!--配置業務層類--> <bean id="accountService" class="com.spring.demo3.AccountServiceImp"> <property name="accountDao" ref="accountDao"/> </bean> <!--配置Dao的類--> <bean id="accountDao" class="com.spring.demo3.AccountDaoImp"> <property name="dataSource" ref="dataSource"/> </bean> <!--配置事務管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--配置事務的通知:(事務的加強)--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- propagation :事務傳播行爲 isolation :事務的隔離級別 read-only :只讀 rollback-for :發生哪些異常回滾 no-rollback-for :發生哪些異常不回滾 timeout :過時信息 --> <tx:method name="transfer" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!--配置切面--> <aop:config> <!--配置切入點--> <aop:pointcut id="pointcut1" expression="execution(* com.spring.demo3.AccountService+.*(..))"/> <!--配置切面--> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/> </aop:config> </beans>
測試類與1中相同,加強是動態織入的,因此此時注入的仍是accountService。
基於註解的方式須要在業務層上添加一個@Transactional的註解。
以下:
AccountServiceImp.java
package com.spring.demo4; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; /** * Created by zhuxinquan on 17-4-27. * propagation :事務的傳播行爲 * isolation :事務的隔離級別 * readOnly :只讀 * rollbackFor :發生哪些異常回滾 */ @Transactional(propagation = Propagation.REQUIRED) public class AccountServiceImp implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } public void transfer(String out, String in, Double money) { accountDao.outMoney(out, money); int i = 1 / 0; accountDao.inMoney(in, money); } }
此時須要在Spring配置文件中開啓註解事務,打開事務驅動
applicationContext4.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd"> <context:property-placeholder location="classpath:jdbc.properties"/> <!--配置c3p0鏈接池--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.Driver}"/> <property name="jdbcUrl" value="${jdbc.URL}"/> <property name="user" value="${jdbc.USERNAME}"/> <property name="password" value="${jdbc.PASSWD}"/> </bean> <!--配置業務層類--> <bean id="accountService" class="com.spring.demo4.AccountServiceImp"> <property name="accountDao" ref="accountDao"/> </bean> <!--配置Dao的類--> <bean id="accountDao" class="com.spring.demo4.AccountDaoImp"> <property name="dataSource" ref="dataSource"/> </bean> <!--配置事務管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--開啓註解事務 打開事務驅動--> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
測試類與1中相同