JDBC 宣告式交易管理


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>

即使您後來不再需要交易管理,則直接在Bean定義檔中修改配置即可,而不用修改程式進行重新編譯等動作。

宣告式交易管理是利用Spring AOP來達成,所以執行以上的程式時,請記得您的Classpath設定中必須包括spring-aop.jar。