问题 :

  • Spring 事务传播机制是怎么样的,在什么应用场景使用
  • 事务是什么
  • 我们使用的框架可能是Hibernate/JPA或者是Mybatis,都知道的底层是需要一个session/connection对象来帮我们执行操作的。要保证事务的完整性,我们需要多组数据库操作要使用同一个session/connection对象,而我们又知道Spring IOC所管理的对象默认都是单例的,这为啥我们在使用的时候不会引发线程安全问题呢?内部Spring到底干了什么?

概述

事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功。 使用Spring 事务的优势有 :

  • 一致的事务编程接口对应不同的事务API
  • 声明式事务
  • 提供简单的编程式事务API
  • 与 Spring DATA 对接

Spring 事务可以通过两种方式实现事务,一种是  declarative transaction management(声明式事务) ,一种是program-matic transaction management (编程式事务),前者又可以通过两种形式来实现 XML 或是 javaConfig。

本文将会使用 @Transctional 注解的形式来解释Spring 事务。

Spring 事务

声明式事务

declarative transctional 事务主要运用到了AOP 的知识。

The most important concepts to grasp with regard to the Spring Framework’s declarative transaction support are that this support is enabled via AOP proxies and that the transactional advice is driven by metadata (currently XML- or annotation-based). The combination of AOP with transactional metadata yields an AOP proxy that uses a TransactionInterceptor in conjunction with an appropriate PlatformTransactionManager implementation to drive transactions around method invocations.

关于Spring Framework的声明式事务支持,最重要的概念是通过AOP代理启用此支持,并且事务性建议由元数据(当前基于XML或基于注释)驱动。 AOP与事务元数据的组合产生一个AOP代理,该代理使用TransactionInterceptor和适当的PlatformTransactionManager实现来驱动围绕方法调用的事务。

这里所说的与AOP相关,可以通过官方文档的这张图来理解 :

平时要是我们在使用spring事务的时候也可以通过Log看到代理的影子。

PlatformTransactionManager 又是什么呢?为什么它可以驱动底层的数据完成调用事务的动作。我们可以从下图来了解Spring 事务的内部结构。

可以看到Spring 事务管理接口提供了多种底层实现,由 PlatformTransactionManager  平台事务管理者来管理。下面讲一下使用,再通过使用来介绍知识点。

使用

例子中使用的是Spring MVC框架 ,以下是一个Service 的方法实现,mStudentDao 进行的操作都是与数据库相关的。

@Transactional(propagation= Propagation.REQUIRED,isolation= Isolation.DEFAULT,readOnly = true)
@Service
public class BeanTestServiceImpl implements BeanTestService {
@Autowired
private StudentDao mStudentDao; @Transactional(propagation= Propagation.REQUIRED,isolation= Isolation.DEFAULT,readOnly = false)
@Override
public void updateStuName(int id, String name) throws Exception {
mStudentDao.updateStuName(id,"pig");
int i = 5/0;
mStudentDao.updateStuName(id,"dog"); }
}

方法中的第二句明显会抛出异常,查看我们的数据库会发现第一句的更新语句发生了回退(rollback),示例很简单。我们看一下@Transcational 中的三个属性 : propagation , isolation , readOnly .

使用了AOP ,那么注解应该就要在应用在public 的方法上上,假如注解不是运用在public(例如protected或是private)上,

When you use proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactionalannotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. If you need to annotate non-public methods, consider using AspectJ (described later).

默认的@Transaction 设置如下 :

  • The propagation setting is PROPAGATION_REQUIRED.

  • The isolation level is ISOLATION_DEFAULT.

  • The transaction is read-write.

  • The transaction timeout defaults to the default timeout of the underlying transaction system, or to none if timeouts are not supported.

  • Any RuntimeException triggers rollback, and any checked Exception does not.

其中propagation setting  和 isolation level  后面会进一步解释。我们看一下这个还有哪些属性可以设置。

后面几个属性和rollback有关,指定在什么情况下回退。

事务的隔离级别

这是 propagation 的选项。

  • PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
  • PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
  • PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
  • PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
  • PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
  • PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

下面我们学习这几个选项。

Understanding PROPAGATION_REQUIRED(默认)

需要注意的地方:默认情况下,一个新的事务加入外部作用域,默认忽略会本地隔离级别,超时值或只读标志(如果有)。这样的场景就是当前的事务是read-only,新来了一个外部的事务是 read-write 的。

By default, a participating transaction joins the characteristics of the outer scope, silently ignoring the local isolation level, timeout value, or read-only flag (if any). Consider switching the validateExistingTransactions flag to true on your transaction manager if you want isolation level declarations to be rejected when participating in an existing transaction with a different isolation level. This non-lenient mode also rejects read-only mismatches (that is, an inner read-write transaction that tries to participate in a read-only outer scope).

出处

它的使用,引用官方文档中的一段叙述来说明 :

     PROPAGATION_REQUIRED enforces a physical transaction, either locally for the current scope if no transaction exists yet or participating in an existing 'outer' transaction defined for a larger scope. This is a fine default in common call stack arrangements within the same thread (for example, a service facade that delegates to several repository methods where all the underlying resources have to participate in the service-level transaction).

Understanding PROPAGATION_REQUIRES_NEW

这是创建一个新的事务。完全独立于上一个事务的操作。

PROPAGATION_NESTED

PROPAGATION_NESTED uses a single physical transaction with multiple savepoints that it can roll back to. Such partial rollbacks let an inner transaction scope trigger a rollback for its scope, with the outer transaction being able to continue the physical transaction despite some operations having been rolled back. This setting is typically mapped onto JDBC savepoints, so it works only with JDBC resource transactions.

PROPAGATION_NESTED设置了多个保存位置,让它遇到异常时只回滚一小部分,而不是全部,它只能在  JDBC resource transactions中使用。执行逻辑就是假如存在事务则嵌套执行,没有则和 require 一样。

关于Understanding PROPAGATION_REQUIRED 和 Understanding PROPAGATION_REQUIRES_NEW的应用场景,可以看到下面的例子 :

public class FooService {
private Repository repo1;
private Repository repo2; @Transactional(propagation=Propagation.REQUIRES_NEW)
public void provideService() {
repo1.retrieveFoo();
repo2.retrieveFoo();
}
}

我们可以写一个测试来对比它们两者表现的差异。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:/fooService.xml")
public class FooServiceTests { private @Autowired TransactionManager transactionManager;
private @Autowired FooService fooService; @Test
public void testProvideService() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
fooService.provideService();
transactionManager.rollback(status);
// assert repository values are unchanged ...
}
  • Requires new we would expect fooService.provideService() was NOT rolled back since it created it's own sub-transaction.

  • Required we would expect everything was rolled back and backing store unchanged.

例子来自于 Stack

isolation 隔离级别

隔离级别看这篇文章 :Mysql学习

使用场景

  • REQUIRE_NEW : 新建事务,单独提交,单独回滚,不受外层事务影响
  • Nested : 利用还原点来回滚,没有事务创建和创建,轻量级,性能好。(例如在批量处理某个事件,不至于全部回滚,只回滚到某个 savepoint)

原理解析

总结

参考资料

Spring 学习(五)--- 事务(未完成)的更多相关文章

  1. Spring学习8-Spring事务管理

      http://blog.sina.com.cn/s/blog_7ffb8dd501014e0f.html   Spring学习8-Spring事务管理(注解式声明事务管理) 标签: spring注 ...

  2. Spring 学习7 -事务

    1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是 ...

  3. Spring学习之事务注解@Transactional

    今天学习spring中的事务注解,在学习Spring注解事务之前需要明白一些事务的基本概念: 事务:并发控制的单位,是用户定义的一个操作序列.这些操作要么都做,要么都不做,是一个不可分割的工作单位.通 ...

  4. Spring学习8-Spring事务管理(编程式事务管理)

    一.Spring事务的相关知识   1.事务是指一系列独立的操作,但在概念上具有原子性. 比如转账:A账号-100, B账号+100,完成.这两个操作独立是没问题的. 但在逻辑上,要么全部完成,要么一 ...

  5. Spring基础学习(五)—事务管理

    一.事务基本认识 1.事务的概述      为了保证数据库中数据的一致性,数据的操作应当是离散的成组的逻辑单元.当它全部完成时,数据的一致性可以保持,而当这个单元中的一部分操作失败,整个事务应当全部视 ...

  6. Spring学习8-Spring事务管理(AOP/声明式式事务管理)

    一.基础知识普及 声明式事务的事务属性: 一:传播行为 二:隔离级别 三:只读提示 四:事务超时间隔 五:异常:指定除去RuntimeException其他回滚异常.  传播行为: 所谓事务的传播行为 ...

  7. Spring学习8-Spring事务管理(注解式声明事务管理)

    步骤一.在spring配置文件中引入<tx:>命名空间 <beans xmlns="http://www.springframework.org/schema/beans& ...

  8. spring学习(五) ———— 整合web项目(SSM)

    一.SSM框架整合 1.1.整合思路 从底层整合起,也就是先整合mybatis与spring,然后在编写springmvc. 1.2.开发需求 查询商品列表(从数据库中查询) 1.3.创建web工程 ...

  9. Spring学习_day03_事务

    本文为博主辛苦总结,希望自己以后返回来看的时候理解更深刻,也希望可以起到帮助初学者的作用. 转载请注明 出自 : luogg的博客园 谢谢配合! Spring_day03 一.事务 1.1 事务 事务 ...

  10. Spring学习五(JDBC支持)

    Spring的jdbc支持 1配置db.properties,将有关JDBC的信息载入 2bean文件配置数据源,这里用e3p0作为数据源载入db.properties 3配置template的bea ...

随机推荐

  1. PXE+Kickstart无人值守安装系统re

    PXE(Preboot Excute Environment)预启动执行环境,可以让计算机通过网络启动系统,主要用于无人值守安装系统中引导客户端主机安装Linux操作系统. 由于之前有过使用cobbl ...

  2. Java50道经典习题-程序11 求不重复数字

    题目:有1.2.3.4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?分析:可填在百位.十位.个位的数字都是1.2.3.4.组成所有的排列后再去 掉不满足条件的排列. public cla ...

  3. ssh 使用密钥无法登入Linux系统

    今天测试密钥登入linux系统时 出现如下问题: root@compute01:~# ssh alicxxx@xxx.com -p -i alickicxxxxxxx.key @@@@@@@@@@@@ ...

  4. day05.2-一个文件的增删改查实例

    一. 测试程序   #INFO.txt源文件内容 global log 127.0.1 local2 daemon maxconn 256 log 127.0.0.1 local2 info defa ...

  5. 【timeisprecious】【JavaScript 】JavaScript RegExp \W 元字符

    JavaScript>RegExp正则表达式> \W 元字符 1 .From Runnob JavaScript RegExp \W 元字符 定义和用法: \W 元字符用于查找非单词字符. ...

  6. python基础目录

    一.博客链接 1.基础操作 python基础,变量,if语句 while循环/格式化输出/ 逻辑运算/ 编码 /单位转换 列表的操作,元组,range; enumerate dict字典;dict的操 ...

  7. 动态代理方案性能对比 (CGLIB,ASSIT,JDK)

    动态代理工具比较成熟的产品有: JDK自带的,ASM,CGLIB(基于ASM包装),JAVAASSIST, 使用的版本分别为: JDK-1.6.0_18-b07, ASM-3.3, CGLIB-2.2 ...

  8. mysql 多实例

    linux系统下,先用mysql用户帐号的身份建立数据表:/usr/local/webserver/mysql/bin/mysql_install_db --basedir=/usr/local/we ...

  9. 类型转换:static_cast、reinterpret_cast等

    一.隐式类型转换 系统自动进行,不需要程序开发人员介入. int m = 3 + 45.6;// 48 把小数部分截掉,也属于隐式类型转换的一部分 double b = 3 + 45.6; // 48 ...

  10. 类型转换 / BOOL 类型

    /* Swift不允许隐式类型转换, 但可以使用显示类型转换(强制类型转换) OC: int intValue = 10; double doubleValue = (double)intValue; ...