日期:2014-05-16  浏览次数:20453 次

JdbcTemplate与事务

为了实现数据操作的原子性,我们需要在程序中引入事务逻辑,在JdbcTemplate中引入事务机制,在Spring中有两种方式:
1. 代码控制的事务管理
2. 参数化配置的事务管理
下面就这两种方式进行介绍。

代码控制的事务管理
首先,进行以下配置,假设配置文件为(Application-Context.xml):
<beans>
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName">
<value>net.sourceforge.jtds.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:jtds:sqlserver://127.0.0.1:1433/Sample</value>
</property>
<property name="username">
<value>test</value>
</property>
<property name="password">
<value>changeit</value>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransac
tionManager">
<property name="dataSource">
<ref local="dataSource" />
</property>
</bean>
<bean id="userDAO" class="net.xiaxin.dao.UserDAO">
<property name="dataSource">
<ref local="dataSource" />
</property>
<property name="transactionManager">
<ref local="transactionManager" />
</property>
</bean>
</beans>
配置中包含了三个节点:
? dataSource
这里我们采用了apache dhcp组件提供的DataSource实现,并为其配置了
JDBC驱动、数据库URL、用户名和密码等参数。
? transactionManager
针对JDBC DataSource类型的数据源,我们选用了
DataSourceTransactionManager
作为事务管理组件。
如果需要使用基于容器的数据源(JNDI),我们可以采用如下配置:
<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>jdbc/sample</value>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTrans
actionManager"
/>
? userDAO
申明了一个UserDAO Bean,并为其指定了dataSource和
transactionManger资源。
UserDAO对应的代码如下:
public class UserDAO {
private DataSource dataSource;
private PlatformTransactionManager transactionManager;
public PlatformTransactionManager getTransactionManager() {
return transactionManager;
}
public void setTransactionManager(PlatformTransactionManager
transactionManager) {
this.transactionManager = transactionManager;
}
public DataSource executeTestSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void insertUser() {
TransactionTemplate tt =new TransactionTemplate(getTransactionManager());
tt.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
JdbcTemplate jt = new JdbcTemplate(executeTestSource());
jt.update(
"insert into users (username) values ('xiaxin');");
jt.update(
"insert into users (id,username) values(2,
'erica');");
return null;
}
});
}
}

可以看到,在insertUser方法中,我们引入了一个新的模板类:org.springframework.transaction.support.TransactionTemplate。TransactionTemplate封装了事务管理的功能,包括异常时的事务回滚,以及操作成功后的事务提交。和JdbcTemplate一样,它使得我们无需在琐碎的try/catch/finally代码中徘徊。
在doInTransaction中进行的操作,如果抛出未捕获异常将被自动回滚,如果成功执行,则将被自动提交。
这里我们故意制造了一些异常来观察数据库操作是否回滚(通过在第二条语句中更新自增ID字段故意触发一个异常):
编写一个简单的TestCase来观察实际效果:
InputStream is = new FileInputStream("Application-Context.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);
UserDAO userDAO = (UserDAO) factory.getBean("userDAO");
userDAO.insertUser();
相信大家多少觉得上面的代码有点凌乱,Callback类的编写似乎也有悖于日常的编程习惯(虽然笔者觉得这一方法比较有趣,因为它巧妙的解决了笔者在早期自行开发数据访问模板中曾经遇到的问题)。
如何进一步避免上面这些问题?Spring 的容器事务管理机制在这里即体现出其强大的能量。

u 参数化配置的事务管理
在上面的Application-Context.xml增加一个事务代理(UserDAOProxy)配置,同时,由于事务由容器管理,UserDAO不再需要TransactionManager设定,将其移除:

<bean id="UserDAOProxy"
class="org.springframework.transaction.interceptor.Transac
tionProxy