ITEEDU

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。