使用宣告式事務管理的好處是,事務管理不侵入您所開發的組件,具體來說,您的DAO物件不會意識到正在事務管理之中,而且如果您想要改變事務管理策略的話,也只需要在定義檔中重新組態即可。
例如,我們設計的DAO物件:
代碼:
package onlyfun.caterpillar;
import javax.sql.DataSource;
import org.springframework.jdbc.core.*;
public class UserDAO {
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
jdbcTemplate = new JdbcTemplate(dataSource);
}
public void insertUser(User user) {
jdbcTemplate.update("INSERT INTO USER VALUES.....");
....
}
....
}
如上面所示的,我們的DAO物件中並沒有任何與事務管理相關的代碼。要進行宣告式事務管理,一個簡化的方法是使用TransactionProxyFactoryBean,指定要介入的事務管理對象及其方法,例如我們的Bean定義檔如下:
代碼:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/TestDB</value>
</property>
<property name="username">
<value>caterpillar</value>
</property>
<property name="password">
<value>123456</value>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<bean id="userDAO" class="onlyfun.caterpillar.UserDAO">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<bean id="userDAOProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="target">
<ref bean="userDAO"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>
TransactionProxyFactoryBean需要一個transactionManager,由於我們直接使用JDBC,所以這邊使用DataSourceTransactionManager,TransactionProxyFactoryBean是個代理物件,target屬性指定要代理的對象,事務管理會自動介入指定的方法前後,這邊使用transactionAttributes屬性指定,insert*表示指定方法名稱insert開頭的都要納入事務管理,您也可以指定方法全名,如果在方法執行過程中發生錯誤,則所有操作自動撤回,否則正常提交。
insert*等方法上指定了PROPAGATION_REQUIRED,表示在目前的事務中執行操作,如果事務不存在就建立一個新的,相關的常數意義都可以在API文件中TransactionDefinition介面中找到。您可以加上多個事務定義,中間使用逗號 "," 區隔,例如您可以加上唯讀,或者是指定某個例外發生時撤回操作:
代碼:
PROPAGATION_REQUIRED,readOnly,-MyCheckedException
MyCheckedException前面加上"-"時,表示發生指定例外時撤消操作,如果前面加上"+",表示發生例外時立即 提交。
由於userDAO被userDAOProxy代理了,所以我們要作的是取得userDAOProxy,而不是userDAO,例如:
代碼:
ApplicationContext context = new FileSystemXmlApplicationContext("bean.xml");
UserDAO userDAO = (UserDAO) context.getBean("userDAOProxy");
userDAO.insertUser(user);
上面介紹的是簡化過後的宣告式事務管理,您可以不處理一些細節問題即得到Spring事務管理的好處,事實上,您可以設定不同的TransactionInterceptor來得到更多的細節處理,這只需要修改配置檔,其它不用作任何修改,然而這涉及到AOP的概念較多,這邊先不作太多的介紹,而僅先就達成相同功能為目的,將上例的配置檔作個修改作為一個示範,如果您有興趣可以先看看詳細的配置是如何設定的:
代碼:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/TestDB</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value></value>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributeSource">
<value>
onlyfun.caterpillar.UserDAO.insert*=PROPAGATION_REQUIRED
</value>
</property>
</bean>
<bean id="userDAO" class="onlyfun.caterpillar.UserDAO">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<bean id="userDAOProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames">
<value>transactionInterceptor,userDAO</value>
</property>
</bean>
</beans>