事务管理

要和数据库交流,事务管理是必不可少的,刚刚开始学习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, 上述文章中存在错误和不足,望大家指出,会虚心改正. 希望本文对像我一样的初学者有所帮助.