Spring 的宣告式交易管理依賴於它的AOP框架來完成,使用宣告式交易管理的好處是,交易管理不侵入您所開發的組件,具體來說,您的DAO物件不會意識到正在交易 管理之中,事實上也應當如此,因為交易管理是屬於系統層面的服務,而不是業務邏輯的一部份,如果您想要改變交易管理策略的話,也只需要在定義檔中重新組態 即可。
舉個例子來說,可以將使 用 JdbcTemplate 修改一下,在不修改UserDAO類別的情況下,您可以為它加入交易管理的服務,一個簡化的方法是使用TransactionProxyFactoryBean,指定要介入的交易管理對象及其方法,這需要在定義檔案修改,如下所示:
- beans-config.xml
<?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.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/demo</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="proxyInterfaces">
<list>
<value>onlyfun.caterpillar.IUserDAO</value>
</list>
</property>
<property name="target">
<ref bean="userDAO"/>
</property>
<property name="transactionManager">
<ref bean="transactionManager"/>
</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,例如:
- SpringAOPDemo.java
package onlyfun.caterpillar;
import org.springframework.context.ApplicationContext;
import org.springframework.context.
support.FileSystemXmlApplicationContext;
public class SpringDAODemo {
public static void main(String[] args) {
ApplicationContext context =
new FileSystemXmlApplicationContext(
"beans-config.xml");
User user = new User();
user.setName("caterpillar");
user.setAge(new Integer(30));
IUserDAO userDAO =
(IUserDAO) context.getBean("userDAOProxy");
userDAO.insert(user);
user = userDAO.find(new Integer(1));
System.out.println("name: " + user.getName());
}
}
您也可以設定不同的TransactionInterceptor來得到更多的管理細節,例如:
<?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.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/demo</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="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="userDAOProxy"
class="org.springframework.aop.
→ framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<list>
<value>onlyfun.caterpillar.IUserDAO</value>
</list>
</property>
<property name="target">
<ref bean="userDAO"/>
</property>
<property name="interceptorNames">
<value>transactionInterceptor</value>
</property>
</bean>
</beans>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/demo</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="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="userDAOProxy"
class="org.springframework.aop.
→ framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<list>
<value>onlyfun.caterpillar.IUserDAO</value>
</list>
</property>
<property name="target">
<ref bean="userDAO"/>
</property>
<property name="interceptorNames">
<value>transactionInterceptor</value>
</property>
</bean>
</beans>
即使您後來不再需要交易管理,則直接在Bean定義檔中修改配置即可,而不用修改程式進行重新編譯等動作。
宣告式交易管理是利用Spring AOP來達成,所以執行以上的程式時,請記得您的Classpath設定中必須包括spring-aop.jar。