事务管理
要和数据库交流,事务管理是必不可少的,刚刚开始学习Spring框架,被他提供的功能吸引,确实减少了我们的编码量.
Spring中,提供了多种和数据库交流的办法,我们最常用的JDBC, 现在流行的O/R映射,含盖现在所有的和数据库交流的办法. Spring不仅给我们提供了这些方法,同时还对其进行了有效的封装,大大减少我们的公式编码工作.举例说明:
下面一段是我们学习JDBC时候,最常见的代码:
Connection conn =null;
Statement stmt = null;
try {
conn = dataSource.getConnection();
stmt = con.createStatement();
stmt.executeUpdate("UPDATE user SET age = 18 WHERE id = 'erica'");
} finally {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException ex) {
logger.warn("Exception in closing JDBC Statement", ex);
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException ex) {
logger.warn("Exception in closing JDBC Connection", ex);
}
}
我们要为执行语句SQL写很多公式代码,这些代码都是能复制和粘贴.我们再来看看,Spring给我们提供的办法.
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.update("UPDATE user SET age = 10 WHERE id = 'erica'");
就是这样两句,大大简化我们的编码工作量,把我们的精力能更多的集中在数据的逻辑上.
注:这是Spring提供的JDBC访问模版,具有一定的入侵性,依赖于Spring的API.
我们传统编码,不仅要管理数据库连接,还要在try/catch中控制数据库事务,而Spring给我们提供的JdbcTemplate将事务完全封装,我们完全不需要去关心,如果出现异常,就会自动回滚.
关于怎么操作数据库,这里就不多说了,我们关心的是数据库的事务机制,在执行一组SQL的时候,如果出现错误,怎么让数据保持一致性?
Spring提供两种数据库事务管理机制:第一,是我们比较熟悉的编码方式;通过手工编码控制事务提交或者回滚;第二,是声明配置方式,通过配置事务管理,控制事务,我比较推荐使用这种办法.
下面用代码说话:
第一种: 编码方式
public class JdbcTest {
/**
* @param args
*/
protected final Log logger = LogFactory.getLog(getClass());
private DataSource dataSource;
private PlatformTransactionManager transactionManager;
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public PlatformTransactionManager getTransactionManager() {
return transactionManager;
}
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public boolean upDate()
TransactionTemplate tt =new TransactionTemplate(getTransactionManager());
tt.execute(new TransactionCallback(){
public Object doInTransaction(TransactionStatus status) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.update("update user1 set userId='120' where userName='totti'");
logger.info("第一条更成完成!");
jdbcTemplate.update("update user1 set userId='1000' where userName='totti'");
logger.info("第二条更成完成!");
//int a=100/0;
return null;
}
});
return true;
}
}
说明下: dataSource, transactionManager 是通过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.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>net.sourceforge.jtds.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:jtds:sqlserver://127.0.0.1:1433/test</value>
</property>
<property name="username">
<value>sa</value>
</property>
<property name="password">
<value>timeout</value>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
<bean id="jdbcTest" class="com.test.JdbcTest">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
</bean>
</beans>
我们要注意这个类: class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
事务管理全靠他了,根据我们的数据库连接不同,选择合适的事务管理类.
代码中有这句 //int a=100/0; 我们可以人为制造异常,看下数据库的回滚效果.
第二种方式:声明配置方式
我们也可以把事务管理的代码放置到配置文件中,为代码文件减减肥.
代码如下:
public class JdbcTest {
/**
* @param args
*/
protected final Log logger = LogFactory.getLog(getClass());
private DataSource dataSource;
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public boolean upDate() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.update("update user1 set userId='120' where userName='totti'");
logger.info("第一条更成完成!");
jdbcTemplate.update("update user1 set userId='1000' where userName='totti'");
logger.info("第二条更成完成!");
//int a=100/0;
}
return true;
}
}
可以和编码方式比较下,这里我们只有SQL语句.
配置文件:
<?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">
<property name="driverClassName">
<value>net.sourceforge.jtds.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:jtds:sqlserver://127.0.0.1:1433/test</value>
</property>
<property name="username">
<value>sa</value>
</property>
<property name="password">
<value>timeout</value>
</property>
</bean>
<bean id="UserDAOProxy"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="target">
<ref local="jdbcTest" />
</property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="upDate*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
<bean id="jdbcTest" class="com.test.JdbcTest">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
</beans>
我们不需要在代码里注入transactionManager,在bean中,我们声明了一个动态UserDAOProxy,他对我们的事务管理负责,也就是说,我们把代码实现的功能,通过这个bean实现了,把事务放到配置中,让我们的代码逻辑更加清晰.更好维护.
<property name="target">
<ref local="jdbcTest" />
</property>
这个配置需要特别说明下,需要实现事务管理的bean配置到这里.
<property name="transactionAttributes"> 这个节点配置事务管理方式, insert*指的是以 insert开头的方法,都使用PROPAGATION_REQUIRED的事务管理方式.
代码中同样有//int a=100/0;
我们可以自己制造一个异常,看看是否会回滚.
下面是测试代码:
public static void main(String[] args) {
// TODO 自动生成方法存根
ApplicationContext ctx=new FileSystemXmlApplicationContext("src/applicationContext.xml");
JdbcTest action = (JdbcTest)ctx.getBean("UserDAOProxy");
boolean action.upDate();
System.out.print(result);
}
JdbcTest action = (JdbcTest)ctx.getBean("UserDAOProxy");
这句要特别说明下,在编码方式中,这个bean是 JdbcTest. 配置文件中还没有UserDAOProxy ; 声明方式配置事务的时候, 我们要指定的 动态代理类 UserDAOProxy ,这里需要注意下,如果配置错误,数据库怎么也不回滚.我就犯过这样的错误.
本人也才开始学习Spring, 上述文章中存在错误和不足,望大家指出,会虚心改正. 希望本文对像我一样的初学者有所帮助.