Spring的事务管理难点剖析(5):联合军种作战的混乱(一)

2014-11-24 08:46:58 · 作者: · 浏览: 1

Spring抽象的DAO体系兼容多种数据访问技术,它们各有特色,各有千秋。像Hibernate是非常优秀的ORM实现方案,但对底层SQL的控制不 太方便;而iBatis则通过模板化技术让你方便地控制SQL,但没有Hibernate那样高的开发效率;自由度最高的当然是直接使用Spring JDBC了,但它也是底层的,灵活的代价是代码的繁复。很难说哪种数据访问技术是最优秀的,只有在某种特定的场景下才能给出答案。所以在一个应用中,往往 采用多个数据访问技术:一般是两种,一种采用ORM技术框架,而另一种采用偏JDBC的底层技术,两者珠联璧合,形成联合军种,共同御敌。
但是,这种联合军种如何应对事务管理的问题呢?我们知道Spring为每种数据访问技术提供了相应的事务管理器,难道需要分别为它们配置对应的事务管理器吗?它们到底是如何协作和工作的呢?这些层出不穷的问题往往压制了开发人员使用联合军种的想法。
其实,在这个问题上,我们低估了Spring事务管理的能力。如果你采用了一个高端ORM技术(Hibernate、JPA、JDO),同时采用一个 JDBC技术(Spring JDBC、iBatis),由于前者的会话(Session)是对后者连接(Connection)的封装,Spring会“足够智能地”在同一个事务线 程让前者的会话封装后者的连接。所以,我们只要直接采用前者的事务管理器就可以了。表10-1给出了混合数据访问技术框架所对应的事务管理器。

序 号
混合数据访问技术框架 事务管理器

1 Hibernate+ Spring JDBC或iBatis
org.springframework.orm.hibernate3.HibernateTransactionManager

2 JPA+Spring JDBC或iBatis org.springframework.orm.jpa.JpaTransactionManager

3 JDO+Spring JDBC或iBatis org.springframework.orm.jdo.JdoTransactionManager


Hibernate+Spring JDBC混合框架的事务管理

由于一般不会出现同时使用多个ORM框架的情况(如Hibernate+JPA),我们不拟对此命题展开论述,只重点研究ORM框架+JDBC框架的情 况。Hibernate+Spring JDBC可能是被使用得最多的组合,本节我们通过实例观察事务管理的运作情况。

01 package com.baobaotao.mixdao;

02

03 …

04

05 @Service("userService")

06 public class UserService extends BaseService {

07 @Autowired

08 private HibernateTemplate hibernateTemplate;

09

10 @Autowired

11 private ScoreService scoreService;

12

13 public void logon(String userName) {

14

15 //①通过Hibernate技术访问数据

16 System.out.println("before updateLastLogonTime()..");

17 updateLastLogonTime(userName);

18 System.out.println("end updateLastLogonTime()..");

19

20 //②通过JDBC技术访问数据

21 System.out.println("before scoreService.addScore()..");

22 scoreService.addScore(userName, 20);

23 System.out.println("end scoreService.addScore()..");

24 }

25

26 public void updateLastLogonTime(String userName) {

27 User user = hibernateTemplate.get(User.class,userName);

28 user.setLastLogonTime(System.currentTimeMillis());

29 hibernateTemplate.update(user);

30

31 //③这句很重要,请看下文的分析

32 hibernateTemplate.flush();

33 }

34 }

在①处,使用Hibernate操作数据,而在②处调用ScoreService#addScore(),该方法内部使用Spring JDBC操作数据。
在③处,我们显式调用了flush()方法,将Session中的缓存同步到数据库中(即马上向数据库发送一条更新记录的SQL语句)。之所以要显式执行 flush()方法,原因是在默认情况下,Hibernate对数据的更改只是记录在一级缓存中,要等到事务提交或显式调用flush()方法时才会将一 级缓存中的数据同步到数据库中,而提交事务的操作发生在 logon()方法返回前。如果所有针对数据库的更改操作都使用Hibernate,这种数据同步的延迟机制并不会产生任何问题。但是,我们在 logon()方法中同时采用了Hibernate和Spring JDBC混合数据访问技术,Spring JDBC无法自动感知Hibernate一级缓存,所以如果不及时调用flush()方法将记录数据更改的一级缓存同步到数据库中,则②处通过 Spring JDBC进行数据更改的结果将被Hibernate一级缓存中的更改覆盖掉,因为Hibernate一级缓存要等到logon()方法返回前才同步到数据 库!
ScoreService使用Spring JDBC数据访问技术,其代码如下所示:

01 package com.baobaotao.mixdao;

02

03 import org.springframework.beans.factory.annotation.Autowired;

04 import org.springframework.jdbc.core.JdbcTemplate;

05 import org.springframework.stereotype.Service;

06 import org.apache.commons.dbcp.BasicDataSource;

07

08 @Service("scoreService")

09 public class ScoreService exten