Spring事务传播特性的浅析——事务方法嵌套调用的迷茫
Spring事务传播机制回顾
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进行了事务增强,则它们工作于同一事务中。来看具体的代码:
package com.baobaotao.nestcall;
…
@Service("userService")
public class UserService extends BaseService {
@Autowired
private JdbcTemplate jdbcTemplate; @Autowired
private ScoreService scoreService; //①该方法嵌套调用了本类的其他方法及其他服务类的方法
public void logon(String userName) {
System.out.println("before userService.updateLastLogonTime...");
updateLastLogonTime(userName);//①-1本服务类的其他方法
System.out.println("after userService.updateLastLogonTime..."); System.out.println("before scoreService.addScore...");
scoreService.addScore(userName, 20); //①-2其他服务类的其他方法
System.out.println("after scoreService.addScore..."); }
public void updateLastLogonTime(String userName) {
String sql = "UPDATE t_user u SET u.last_logon_time = ? WHERE user_name =?";
jdbcTemplate.update(sql, System.currentTimeMillis(), userName);
}
UserService中注入了ScoreService的Bean,而ScoreService的代码如下所示:
package com.baobaotao.nestcall;
…
@Service("scoreUserService")
public class ScoreService extends BaseService{ @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);
}
}
通过Spring配置为ScoreService及UserService中所有公有方法都添加Spring AOP的事务增强,让UserService的logon()和updateLastLogonTime()及ScoreService的addScore()方法都工作于事务环境下。下面是关键的配置代码:
<?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" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<context:component-scan base-package="com.baobaotao.nestcall"/>
…
<bean id="jdbcManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"/> <!--①通过以下配置为所有继承BaseService类的所有子类的所有public方法都添加事务增强-->
<aop:config proxy-target-class="true">
<aop:pointcut id="serviceJdbcMethod"
expression="within(com.baobaotao.nestcall.BaseService+)"/>
<aop:advisor pointcut-ref="serviceJdbcMethod" advice-ref="jdbcAdvice" order="0"/>
</aop:config>
<tx:advice id="jdbcAdvice" transaction-manager="jdbcManager">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
</beans>
将日志级别设置为DEBUG,启动Spring容器并执行UserService#logon()的方法,仔细观察如下输出日志:
before userService.logon method...
//①创建了一个事务
Creating new transaction with name [com.baobaotao.nestcall.UserService.logon]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Acquired Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver] for JDBC transaction
Switching JDBC Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver] to manual commit
before userService.updateLastLogonTime...
<!--②updateLastLogonTime()和logon()在同一个Bean中,并未发生加入已存在事务上下文的
动作,而是“天然”地工作于相同的事务上下文-->
Executing prepared SQL update
Executing prepared SQL statement [UPDATE t_user u SET u.last_logon_time = ? WHERE user_name =?]
SQL update affected 1 rows
after userService.updateLastLogonTime...
before scoreService.addScore...
//③ScoreService#addScore方法加入到①处启动的事务上下文中
Participating in existing transaction
Executing prepared SQL update
Executing prepared SQL statement [UPDATE t_user u SET u.score = u.score + ? WHERE user_name =?]
SQL update affected 1 rows
after scoreService.addScore...
Initiating transaction commit
Committing JDBC transaction on Connection [jdbc:mysql://localhost:3306/sampledb, UserName=root@localhost, MySQL-AB JDBC Driver]
…
after userService.logon method...
从上面的输出日志中,可以清楚地看到Spring为UserService#logon()方法启动了一个新的事务,而UserSerive#updateLastLogonTime()和UserService#logon()是在相同的类中,没有观察到有事务传播行为的发生,其代码块好像“直接合并”到UserService#logon()中。
然而在执行到ScoreService#addScore()方法时,我们就观察到发生一个事务传播的行为:" Participating in existing transaction ",
这说明ScoreService#addScore()添加到UserService#logon()的事务上下文中,两者共享同一个事务。
所以最终的结果是UserService的logon()、updateLastLogonTime()以及ScoreService的addScore都工作于同一事务中。
注:以上内容摘自《Spring 3.x企业应用开发实战》
http://blog.csdn.net/hy6688_/article/details/44763869
Spring事务传播特性的浅析——事务方法嵌套调用的迷茫的更多相关文章
- 什么是事务?事务特性?事务隔离级别?spring事务传播特性?
一.事务的概述 什么是事务? 在数据库中,所谓事务是指一组逻辑操作单元即一组sql语句,当这个单元中的一部分操作失败,整个事务回滚,只有全部正确才完成提交.判断事务是否配置成功的关键点在于出现异常时事 ...
- 事务、事务特性、事务隔离级别、spring事务传播特性
事务.事务特性.事务隔离级别.spring事务传播特性 1.什么是事务: 事务是程序中一系列严密的操作,所有操作执行必须成功完成,否则在每个操作所做的更改将会被撤销,这也是事务的原子性(要么成功, ...
- 什么是事务、事务特性、事务隔离级别、spring事务传播特性
1.什么是事务: 事务是程序中一系列严密的操作,所有操作执行必须成功完成,否则在每个操作所做的更改将会被撤销,这也是事务的原子性(要么成功,要么失败). 2.事务特性: 事务特性分为四个:原子性(At ...
- spring事务传播特性实验(2):PROPAGATION_REQUIRED实验结果与分析
本文延续上一文章(spring事务传播特性实验(1):数据准备),在已经准备好环境的情况下,做如下的实验,以验证spring传播特性,加深对spring传播特性的理解. 本次主要验证PROPAGATI ...
- 【原创】spring中的事务传播特性
关于spring的传播特性,我对其进行了详细的叙述了下: PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务.这是最常见的选择. 比如方法A调用方法B,如果方法 ...
- 玩转Spring全家桶笔记 04 Spring的事务抽象、事务传播特性、编程式事务、申明式事务
1.Spring 的事务抽象 Spring提供了一致的事务模型 JDBC/Hibernate/Mybatis 操作数据 DataSource/JTA 事务 2.事务抽象的核心接口 PlatformTr ...
- Spring 事物传播特性
Spring 事物传播特性 这是Spring官方的定义 一共有7种 摘自源码省略了一部分 public interface TransactionDefinition { int PROPAGATIO ...
- Spring AOP开发时如何得到某个方法内调用的方法的代理对象?
Spring AOP开发时如何得到某个方法内调用的方法的代理对象? 问题阅读起来拗口,看代码 在方法中调用其他方法很常见,也经常使用,如果在一个方法内部调用其他方法,比如 public class U ...
- 解惑spring事务传播特性之嵌套事务
/** * Support a current transaction, create a new one if none exists. * Analogous to EJB transaction ...
随机推荐
- Lisp与JAVA的酷毙结合——abcl
最近看了一本叫做<黑客与画家>的书,其中对于Lisp语言大加褒奖.自己试着用了一下,虽然确实有反人类之嫌,但是确实是一门不错的语言,New Architect杂志上有一篇介绍ITA软件公司 ...
- 06链队列_LinkQueue--(栈与队列)
#include "stdio.h" #include "stdlib.h" #include "io.h" #include " ...
- 【制作镜像Win*】特殊配置
下载disk.config: wget http://10.254.3.75/images/configdrive/disk.config 修改libvirt.xml,更换cdrom为disk.con ...
- wait(...) notify() notifyAll()
简介 wait.notify.notifyAll是Java中3个与线程有关的方法,它们都是Object类中的方法. 其中,wait方法有3个重载形式: 1.wait() 2.wait(long tim ...
- Poj/OpenJudge 1000 A+b Problem C/C++/Java
1.题意翻译: 输入两个整数a,b (0<=a,b<=10),计算a+b的值并输出. 其中还提到输出不能有多余的内容,即不能加多空格符号等内容,不然会报Wrong ...
- [翻译][MVC 5 + EF 6] 6:创建更复杂的数据模型
原文:Creating a More Complex Data Model for an ASP.NET MVC Application 前面的教程中,我们使用的是由三个实体组成的简单的数据模型.在本 ...
- checked
<!doctype html><html lang="en"> <head> <meta charset="UTF-8" ...
- 对WebClient扩展自动解压缩页面
WebClient下载压缩网页时出现的全是乱码,可通过扩展来解决这个问题. public class MyWebClient : WebClient { protected override WebR ...
- C# 制作Zip压缩包
压缩包制作也是很多项目中需要用到的功能.比如有大量的文件(假设有10000个)需要上传,1个1个的上传似乎不太靠谱(靠,那得传到什么时候啊?),这时我们可以制作一个压缩包zip,直接传这个文件到服务器 ...
- Entity Framework 插入数据 解决主键非自增问题
http://blog.csdn.net/educast/article/details/8632806 与Entity Framework相伴的日子痛并快乐着.今天和大家分享一下一个快乐,两个痛苦. ...