Spring的事務管理難點剖析(1):DAO和事務管理的牽絆

有些人不多使用Spring而不使用Spring事務管理器的應用,所以經常有人會問:是否用了Spring,就必定要用Spring事務管理器,不然就沒法進行數據的持久化操做呢?事務管理器和DAO是什麼關係呢? 也許是DAO和事務管理形影不離的緣故吧,這個看似簡單的問題實實在在地存在着,從初學者心中涌出,縈繞在老手的腦際。答案固然是否認的!咱們都知道:事務管理是保證數據操做的事務性(即原子性、一致性、隔離性、持久性,即所謂的ACID),脫離了事務性,DAO照樣能夠順利地進行數據的操做。 java


  JDBC訪問數據庫

  下面,咱們來看一段使用Spring JDBC進行數據訪問的代碼:
  spring

package com.baobaotao.withouttx.jdbc; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.apache.commons.dbcp.BasicDataSource; @Service("userService") public class UserJdbcWithoutTransManagerService { @Autowired private JdbcTemplate jdbcTemplate; public void addScore(String userName,int toAdd){ String sql = "UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?"; jdbcTemplate.update(sql,toAdd,userName); } public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("com/baobaotao/withouttx/jdbc/jdbcWithoutTx.xml"); UserJdbcWithoutTransManagerService service = (UserJdbcWithoutTransManagerService)ctx.getBean("userService"); JdbcTemplate jdbcTemplate = (JdbcTemplate)ctx.getBean("jdbcTemplate"); BasicDataSource basicDataSource = (BasicDataSource)jdbcTemplate.getDataSource(); //①檢查數據源autoCommit的設置 System.out.println("autoCommit:"+ basicDataSource.getDefaultAutoCommit()); //②插入一條記錄,初始分數爲10 jdbcTemplate.execute("INSERT INTO t_user(user_name,password,score,last_logon_time) VALUES('tom','123456',10,"+System.currentTimeMillis()+")"); //③調用工做在無事務環境下的服務類方法,將分數添加20分 service.addScore("tom",20); //④查看此時用戶的分數 int score = jdbcTemplate.queryForInt( "SELECT score FROM t_user WHERE user_name ='tom'"); System.out.println("score:"+score); jdbcTemplate.execute("DELETE FROM t_user WHERE user_name='tom'"); } } 


  其中,jdbcWithoutTx.xml的配置文件以下所示: sql

<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="com.baobaotao.withouttx.jdbc"/> <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}" p:username="${jdbc.username}" p:password="${jdbc.password}"/> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="dataSource"/> </beans>

運行UserJdbcWithoutTransManagerService,在控制檯上打出以下的結果:數據庫

引用

defaultAutoCommit:true
score:30

   在jdbcWithoutTx.xml中,沒有配置任何事務管理器,可是數據已經成功持久化到數據庫中。在默認狀況下,dataSource數據源的 autoCommit被設置爲true——這也意謂着全部經過JdbcTemplate執行的語句立刻提交,沒有事務。若是將dataSource的 defaultAutoCommit設置爲false,再次運行UserJdbcWithoutTransManagerService,將拋出錯誤,原 因是新增及更改數據的操做都沒有提交到數據庫,因此代碼清單10-1④處的語句因沒法從數據庫中查詢到匹配的記錄而引起異常。
   對於強調讀速度的應用,數據庫自己可能就不支持事務:如使用MyISAM引擎的MySQL數據庫。這時,無須在Spring應用中配置事務管理器,由於即便配置了,也是沒有實際用處的。

Hibernate訪問數據庫

   對於Hibernate來講,狀況就有點複雜了。由於Hibernate的事務管理擁有其自身的意義,它和Hibernate一級緩存在密切的關係:當我 們調用Session的save、update等方法時,Hibernate並不直接向數據庫發送SQL語句,只在提交事務(commit)或flush 一級緩存時才真正向數據庫發送SQL。因此,即便底層數據庫不支持事務,Hibernate的事務管理也是有必定好處的,不會對數據操做的效率形成負面影 響。因此,若是是使用Hibernate數據訪問技術,沒有理由不配置HibernateTransactionManager事務管理器。

   可是,不使用Hibernate事務管理器,在Spring中,Hibernate照樣也能夠工做,來看下面的例子: apache

package com.baobaotao.withouttx.hiber; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.orm.hibernate3.HibernateTemplate; import org.apache.commons.dbcp.BasicDataSource; import org.springframework.test.jdbc.SimpleJdbcTestUtils; import com.baobaotao.User; @Service("hiberService") public class UserHibernateWithoutTransManagerService { @Autowired private HibernateTemplate hibernateTemplate; public void addScore(String userName,int toAdd){ User user = hibernateTemplate.get(User.class,userName); user.setScore(user.getScore()+toAdd); hibernateTemplate.update(user); } public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("com/baobaotao/withouttx/hiber/hiberWithoutTx.xml"); UserHibernateWithoutTransManagerService service = (UserHibernateWithoutTransManagerService)ctx.getBean("hiberService"); JdbcTemplate jdbcTemplate = (JdbcTemplate)ctx.getBean("jdbcTemplate"); BasicDataSource basicDataSource = (BasicDataSource)jdbcTemplate.getDataSource(); //①檢查數據源autoCommit的設置 System.out.println("autoCommit:"+ basicDataSource.getDefaultAutoCommit()); //②插入一條記錄,初始分數爲10 jdbcTemplate.execute("INSERT INTO t_user(user_name,password,score,last_logon_time) VALUES('tom','123456',10,"+System.currentTimeMillis()+")"); //③調用工做在無事務環境下的服務類方法,將分數添加20分 service.addScore("tom",20); //④查看此時用戶的分數 int score = jdbcTemplate.queryForInt( "SELECT score FROM t_user WHERE user_name ='tom'"); System.out.println("score:"+score); jdbcTemplate.execute("DELETE FROM t_user WHERE user_name='tom'"); } }


此時,採用hiberWithoutTx.xml的配置文件,其配置內容以下所示: 緩存

<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="com.baobaotao.withouttx.hiber"/> … <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" p:dataSource-ref="dataSource"> <property name="annotatedClasses"> <list> <value>com.baobaotao.User</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> </props> </property> </bean> <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate" p:sessionFactory-ref="sessionFactory"/> </beans>


com.baobaotao.User是使用了Hibernate註解的領域對象,代碼以下所示: session

package com.baobaotao; import javax.persistence.Entity; import javax.persistence.Table; import javax.persistence.Column; import javax.persistence.Id; import java.lang.reflect.Field; import java.io.Serializable; @Entity @Table(name="T_USER") public class User implements Serializable{ @Id @Column(name = "USER_NAME") private String userName; private String password; private int score; @Column(name = "LAST_LOGON_TIME") private long lastLogonTime = 0; … }

  運行UserHibernateWithoutTransManagerService,程序正確執行,並獲得相似於 UserJdbcWithoutTransManagerService的執行結果。這說明Hibernate在Spring中,在沒有事務管理器的狀況 下,依然能夠正常地進行數據的訪問。

  注:以上內容摘自《Spring 3.x企業應用開發實戰》 url

相關文章
相關標籤/搜索