问题 :

  • 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. 解决 UnicodeEncodeError: 'ascii' codec can't encode characters in position 问题

    在开头加上 import sys reload(sys) sys.setdefaultencoding( "utf-8" ) Python自然调用ascii编码解码程序去处理字符流 ...

  2. WPF 背景网格图

    利用DrawingBrush来画出背景网格图 <DrawingBrush Viewport="0,0,80,80" ViewportUnits="Absolute& ...

  3. NOI2019省选模拟赛 第三场

    传送门 明明没参加过却因为点进去结果狂掉\(rating\)-- \(A\) 集合 如果我们记 \[f_k=\sum_{i=1}^nT^i{n-i\choose k}\] 那么答案显然就是\(f_{k ...

  4. Python 开发安卓Android及IOS应用库Kivy安装尝试

    Python 开发安卓Android及IOS应用库Kivy安装尝试: 先来看看这货可以用来制作什么应用: Create a package for Windows Create a package f ...

  5. jquery源码解析:jQuery数据缓存机制详解2

    上一课主要讲了jQuery中的缓存机制Data构造方法的源码解析,这一课主要讲jQuery是如何利用Data对象实现有关缓存机制的静态方法和实例方法的.我们接下来,来看这几个静态方法和实例方法的源码解 ...

  6. [Objective-C语言教程]类别(28)

    有时,可能会发现希望通过添加仅在某些情况下有用的行为来扩展现有类. 要向现有类添加此类扩展,Objective-C提供了类别和扩展. 如果需要向现有类添加方法,或许为了添加功能以便在应用程序中更容易地 ...

  7. 注解中用于@target的方法annotation/--ElementType.METHOD,ElementType.TYPE对应方法,类接

    @Target: @Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages.types(类.接口.枚举.Annotation类型).类型成员(方法.构造 ...

  8. 苹果隐私条例更新:收集用户电话和 Email 数据

    简评:苹果现在会收集用户的电话和电子邮件,作为用户「信任评级」的一部分,我还是支持的,因为园长被黑产攻击 AppleID,直接刷爆了我的卡!但是在大环境看,隐私已经不存在了. Apple 最近悄悄为 ...

  9. KCF+Opencv3.0+Cmake+Win10

    配置 需要的文件下载 安装CMake,安装opencv3.0.0 在KCFcpp-master 目录下新建一个文件夹,命名为build 打开CMake-GUI配置如下: 点击Configure,编译器 ...

  10. Maven 问题笔记汇总

    Web项目通过Maven部署到Tomcat的错误. Maven环境下面多项目之间的引用 Maven 远程仓库下载慢的的解决方案 Intellij IDEA 像eclipse那样给maven添加依赖 I ...