问题 :

  • 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. Kylin性能调优记——业务技术两手抓

    本文由  网易云发布. 作者:冯宇 本篇文章仅限内部分享,如需转载,请联系网易获取授权. 背景 最近开始使用了新版本的Kylin,在此之前对于新版本的了解只是代码实现和一些简单的新功能测试,但是并没有 ...

  2. Git 常用知识

    git 常用命令 创建并checkout分支: git checkout -b branch_name git merge 与 git rebase 的区别 git rebase 合并后将形成一条直线 ...

  3. Delphi XE7中使用JSON

    Delphi XE7有一个对JSON处理的单元,在你需要使用JSON的单元里面引入"System.json",随后你就可以用Delphi自己的json处理类了.我写的小例子只是对包 ...

  4. AngularJS入门讲解1:angular基本概念

    AngularJS应用程序主要有三个组成部分: 模板(Templates) 模板是您用HTML和CSS编写的文件,展现应用的视图. 您可给HTML添加新的元素.属性标记,作为AngularJS编译器的 ...

  5. IE 8-不支持 placeholder 解决方法

    ;!function fixPlaceholder() { var hasPlaceholder = 'placeholder' in document.createElement('input'); ...

  6. [转]NSProxy实现AOP方便为ios应用实现异常处理策略

    [转载自:http://blog.csdn.net/yanghua_kobe/article/details/8395535] 前段时间关注过objc实现的AOP,在GitHub找到了其中的两个库:A ...

  7. django在model中添加字段报错

    在以下类中添加 description 字段后, class Colors(models.Model): colors = models.CharField(u'颜色', max_length=10) ...

  8. RDD转换成为DataFrame

    方式一: 通过case class创建DataFrames(反射) TestDataFrame1.scala package com.bky // 隐式类的导入 // 定义case class,相当于 ...

  9. 详细解读KMP模式匹配算法

    转载请注明出处:http://blog.csdn.net/fightlei/article/details/52712461 首先我们需要了解什么是模式匹配? 子串定位运算又称为模式匹配(Patter ...

  10. 本地搭建sass运行环境

    1.安装node.js 安装文件为msi文件,可到node.js官网下载安装包,下载路径为:https://nodejs.org/en/download/ 安装路径为默认路径,安装完成之后配置环境变量 ...