在開發中需要操作數據庫,進行增、刪、改操作的過程中屬於一次操作,如果在一個業務中需要更新多張表,那麼任意一張表的更新失敗,整個業務的更新就是失敗,這時那些更新成功的表必須回滾,否則業務會出錯,這時就要用到事務,即這個業務的操作屬於一個事務,事務具有原子性、隔離性、一致性、持續性。這時便用到了事務,事務控制的目的是保證一組操作要麼全部成功,要麼全部失敗。spring提供了對事務的支持,在spring中主要有兩種方式使用事務,一、編程式事務控制;二、聲明式事務控制。
一、編程式事務控制
所謂編程式事務控制即通過編寫代碼的方式實現事務的控制。
spring為了方便處理事務,提供了事務管理器,對事務的控制歸根到底是通過事務管理器進行控制,在spring中所有的事務控制必須要有事務管理器。下面是一個編程式事務控制的例子,實現賬戶之間的轉賬,我們把對事務的控制放在系統的service層(分為controller層、service層、DAO層)來處理,下面是我的spring配置文件,
<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd" > <!--spring 自動檢測--> <context:component-scan base-package="com.cn.study.day5" /> <!----> <context:property-placeholder location="classpath:db.properties"/> <!----> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>${db.driver}</value> </property> <property name="url"> <value>${db.url}</value> </property> <property name="username"> <value>${db.username}</value> </property> <property name="password"> <value>123456</value> </property> </bean> <bean id="dao" class="com.cn.study.day5.service.inter.impl.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <!--配置事務管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!--事務管理器模板 方便使用事務--> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"></property> </bean> </beans>
配置了事務管理器,這裡使用DataSourceTransactionManager,事務管理器有一個dataSource屬性必須配置,這裡使用ref屬性引用上邊的。有了事務管理器之後要使用事務還是比較麻煩,spring又提供了事務管理器模板,我們配置事務管理器模板,事務管理器模板需要一個事務管理器屬性,我們引用上邊的事務管理器。至此關於編程式的事務控制的配置文件已經准備完畢,下面進行編程式開發。由於,我們把事務控制放在service層,下面是我的service層的代碼,
package com.cn.study.day5.service.inter.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import com.cn.study.day5.service.inter.AccountDaoInter; import com.cn.study.day5.service.inter.AccountServiceIter; @Component public class AccountServiceImpl implements AccountServiceIter { @Autowired private AccountDaoInter adi; @Autowired private TransactionTemplate tt; //轉賬方法,由out向in轉money元 @Override public void transfer(final String out, final String in, final double money) { // TODO Auto-generated method stub //使用事務管理器模板進行事務控制 tt.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { // TODO Auto-generated method stub adi.outMoney(out, money); //一個異常,使用了事務控制,在出現了異常之後,事務會回滾 int i = 1 / 0; adi.inMoney(in, money); } }); } }
由於是面向接口編程,這裡我只貼出了service層的實現,使用了自動掃描機制(掃描類、屬性上的注解@Component、@Autowired),transfer方法是實現轉賬的方法,首先從一個賬戶轉出,然後轉入另一個賬戶,使用事務管理器模板的execute方法,需要一個TransactionCallBack的實例,這裡使用匿名內部類的方式,把要執行的方法放在doInTransactionWithoutResult中執行,保證了事務的控制。
使用這種方式可以保證事務控制,但是在實際開發過程當中這種方式對代碼的改動太大,不符合低侵入開發原則,所有這種方式在開發中幾乎很少用到,用的最多的是聲明式的事務控制。
二、聲明式事務控制
聲明式事務控制又分為三種方式,一、基於TransactionProxyFactoryBean代理的聲明式事務控制;二、使用AOP的聲明式事務控制;三、基於@Transactional注解的聲明式事務控制。
1、基於TransactionProxyFactoryBean的聲明式事務控制
TransactionProxyFactoryBean是事務的代理類,spring會為目標類生成一個代理,具體的配置如下,
<!--配置事務管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!--配置業務層代理--> <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="target" ref="accountServiceImpl"></property> <property name="transactionManager" ref="transactionManager"></property> <property name="transactionAttributes"> <props> <prop key="transfer"></prop> </props> </property> </bean>
這裡只貼出了事務管理器和業務層代理的配置,剩余的數據源和業務類的配置可以執行配置,前面說到無論使用哪種方式配置事務管理,都需要使用事務管理器。重點看業務層代理,配置的class屬性為TransactionProxyFactoryBean,需要配置三個屬性:target(要代理的具體業務層實現類)、transactionManager(事務管理器)、transactionAttributes(要攔截的業務層方法)。配置完成之後,便可以進行測試,測試代碼如下,
ApplicationContext ac=getApplicationContext(); AccountServiceIter asi=(AccountServiceIter)ac.getBean("accountServiceProxy"); asi.transfer("aa", "cc", 10d);
通過getApplicationContext()方法獲得了ApplicationContext實例,然後獲得accountServiceProxy的實例,這裡獲得的不是AccountServiceImpl的實例而是代理的實例對象,因為使用代理,代理了實際的業務類,所有這裡不能再使用實際的類而應是代理類。
使用這種方式的需要為每一個需要使用事務的業務類配置一個代理比教麻煩,所以在開發過程中這種方式幾乎不用。
2、使用AOP的聲明式事務控制
這種方式是在開發過程中使用的比較多的一種,配置如下,
<!--配置事務管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!--配置事務增強--> <tx:advice id="advicer" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="transfer*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!--配置切點、事務通知--> <aop:config> <aop:pointcut id="myPointcut" expression="execution(* com.cn.study.day555.service.inter.impl.*.*(..))"/> <aop:advisor advice-ref="advicer" pointcut-ref="myPointcut"/> </aop:config>
配置了事務增強<tx:advice>配置對要增強的方法的事務的傳播行為等,配置<aop:config>配置切點和對應的事務通知,這樣就完成了AOP的聲明式事務控制。
3、基於@Transactional注解
使用@Transactional注解需要再配置文件中開啟對這個注解的掃描:<tx:annotation-driven transaction-manager="transactionManager" />,引用了事務管理器,然後就可以使用@Transactional注解,此注解可以使用在類上,也可以使用在方法上,使用在類上即對此類的所有方法都起作用,使用在方法上則表示對單個方法起作用,還可以配置一些屬性,放在另一篇文章中進行解釋。
通過對以上四種配置事務的方式的描述,其中聲明式方式中的第二種方式使用比較普通,對代碼的侵入比較小,第三種因為配置簡單,也比較常用,但是需要在業務類或方法上加@Transcational注解,對代碼有一定的侵入。
有不正之處歡迎指出,謝謝!