Spring事务一个被讹传很广说法是:一个事务方法不应该调用另一个事务方法,否则将产生两个事务。结果造成开发人员在设计事务方法时束手束脚,生怕一不小心就踩到地雷。
其实这是不认识Spring事务传播机制而造成的误解,Spring对事务控制的支持统一在TransactionDefinition类中描述,该类有以下几个重要的接口方法:
int getPropagationBehavior():事务的传播行为
int getIsolationLevel():事务的隔离级别
int getTimeout():事务的过期时间
boolean isReadOnly():事务的读写特性
很明显,除了事务的传播行为外,事务的其他特性Spring是借助底层资源的功能来完成的,Spring无非只充当个代理的角色。但是事务的传播行为却是 Spring凭借自身的框架提供的功能,是Spring提供给开发者最珍贵的礼物,讹传的说法玷污了Spring事务框架最美丽的光环。
所谓事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。Spring支持以下7种事务传播行为。
PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务,就加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
Spring默认的事务传播行为是PROPAGATION_REQUIRED,它适合绝大多数的情况,如果多个ServiveX#methodX()均工 作在事务环境下(即均被Spring事务增强),且程序中存在如下的调用 链:Service1#method1()->Service2#method2()->Service3#method3(),那么这3个 服务类的3个方法通过Spring的事务传播机制都工作在同一个事务中。
相互嵌套的服务方法
我们来看一下实例,UserService#logon()方法内部调用了UserService#updateLastLogon Time()和ScoreService#addScore()方法,这两个类都继承于BaseService。它们之间的类结构如下图所示:
UserService#logon()方法内部调用了ScoreService#addScore()的方法,两者都分别通过Spring AOP进行了事务增强,则它们工作于同一事务中。来看具体的代码:
01 package com.baobaotao.nestcall;
02 …
03 @Service("userService")
04 public class UserService extends BaseService {
05 @Autowired
06 private JdbcTemplate jdbcTemplate;
07
08 @Autowired
09 private ScoreService scoreService;
10
11 //①该方法嵌套调用了本类的其他方法及其他服务类的方法
12 public void logon(String userName) {
13 System.out.println("before userService.updateLastLogonTime...");
14 updateLastLogonTime(userName);//①-1本服务类的其他方法
15 System.out.println("after userService.updateLastLogonTime...");
16
17 System.out.println("before scoreService.addScore...");
18 scoreService.addScore(userName, 20); //①-2其他服务类的其他方法
19 System.out.println("after scoreService.addScore...");
20
21 }
22 public void updateLastLogonTime(String userName) {
23 String sql = "UPDATE t_user u SET u.last_logon_time = WHERE user_name = ";
24 jdbcTemplate.update(sql, System.currentTimeMillis(), userName);
25 }
UserService中注入了ScoreService的Bean,而ScoreService的代码如下所示:
01 package com.baobaotao.nestcall;
02 …
03 @Service("scoreUserService")
04 public class ScoreService extends BaseService{
05
06 @Autowired
07 private JdbcTemplate jdbcTemplate;
08
09 public void addScore(String userName, int toAdd) {
10 String sql = "UPDATE t_user u SET u.score = u.score + WHERE user_name = ";
11 jdbcTemplate.update(sql, toAdd, userName);
12 }
13 }
通过Spring配置为ScoreService及UserService中所有公有方法都添加Spring AOP的事务增强,让UserService的logon()和updateLastLogonTime()及ScoreService的 addScore()方法都工作于事务环境下。下面是关键的配置代码:
01 < xml version="1.0" encoding="UTF-8" >
02
03 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
04 xmlns:context="http://www.springframework.org/schema/context"