这篇文章,主要介绍如何使用注解进行事务控制,以及Spring中事务的七种传播机制、Spring事务失效的几个原因。

目录

一、Spring事务控制(注解)

(1)XML开启组件扫描

(2)XML开启事务注解驱动

(3)编写【MoneyService】类

(4)编写测试类

二、Spring中七种传播机制

2.1、什么是传播机制

2.2、七种传播机制

(1)PROPAGATION_REQUIRED

(2)PROPAGATION_SUPPORTS

(3)PROPAGATION_MANDATORY

(4)PROPAGATION_REQUIRES_NEW

(5)PROPAGATION_NOT_SUPPORTED

(6)PROPAGATION_NEVER

(7)PROPAGATION_NESTED

2.3、Spring事务失效情况

(1)方法不是public修饰

(2)A方法通过this调用B方法

(3)数据库不支持事务

(4)对应的类没有被Spring管理

(5)异常被捕获未抛出


一、Spring事务控制(注解)

Spring框架从2.5版本之后,就开始支持使用注解进行开发了,注解开发可以减少很多XML配置信息,提高程序的一个可读性以及开发效率,下面我们看下Spring里面如何使用注解开发。

(1)XML开启组件扫描

Spring框架,默认情况下,都是通过在XML配置文件里面,定义一些【<bean>】标签来进行bean对象的管理,以及属性注入,如果我们需要使用注解开发,那么就需要告诉Spring框架,如何让Spring知道我们要采用注解开发呢???

这就需要在XML配置文件里面,显式的添加一个【组件扫描】的配置,有了这个配置,Spring启动加载XML配置文件时候,就可以读取到这个【组件扫描】,Spring就会去对应的包下面扫描相关的注解,这时候Spring才可以使用注解开发。

<!-- 开启组件扫描 -->
<context:component-scan base-package="com.spring.demo"/>
  • base-package属性:指定扫描哪些包路径下的类。

(2)XML开启事务注解驱动

开启组件扫描后,我们的事务还是不能够使用,还需要额外的开启事务注解驱动【<tx:annotation-driven>】,注解驱动的作用是:告诉Spring框架去扫描【@Transactional】注解,然后给对应的方法添加事务控制功能。

<!-- 开启事务注解驱动 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
  • transaction-manager属性:指定采用哪个事务管理器(如果事务管理器的beanName叫做【transactionManager】,那么可以省略这个属性,因为这个属性默认值就是【transactionManager】)

完整XML配置信息:

<?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:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-context.xsd"><!-- 开启组件扫描 --><context:component-scan base-package="com.spring.demo"/><!-- 引入 jdbc.properties 文件 --><context:property-placeholder location="jdbc.properties"/><!-- 配置数据源 --><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><!-- 数据库驱动 --><property name="driverClassName" value="${jdbc.driver}"/><!-- 数据库地址 --><property name="url" value="${jdbc.url}"/><!-- 用户名称 --><property name="username" value="${jdbc.username}"/><!-- 用户密码 --><property name="password" value="${jdbc.password}"/></bean><!-- 配置 JdbcTemplate 模板 --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><!-- 指定数据源: 告诉 JdbcTemplate 需要操作哪个数据库 --><property name="dataSource" ref="dataSource"/></bean><!-- 配置事务管理器因为Spring里面可以有多少个事务一起运行,所以为了管理方便,可以采用事务管理器统一的管理所有事务--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!-- 指定数据源 --><property name="dataSource" ref="dataSource"/></bean><!-- 开启事务注解驱动 --><tx:annotation-driven transaction-manager="transactionManager"/></beans>

(3)编写【MoneyService】类

@Component // 将当前这个Bean交给IOC容器管理
public class MoneyService {private JdbcTemplate jdbcTemplate;public MoneyService(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}@Transactional // 开启事务注解public void transferMoney() {// A用户减少1000元String sql = "UPDATE spring.t_money SET balance = balance - 1000  WHERE id = 10001";int update = jdbcTemplate.update(sql);System.out.println("A用户减少1000元成功: " + update);// 模拟转账过程中出现异常int i = 10 / 0;// B用户增加1000元sql = "UPDATE spring.t_money SET balance = balance + 1000  WHERE id = 10002";update = jdbcTemplate.update(sql);System.out.println("B用户减少1000元成功: " + update);}
}

(4)编写测试类

public class TestTx02 {public static void main(String[] args) {// 1、读取配置文件ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");// 2、获取对象MoneyService moneyService3 = context.getBean(MoneyService.class);// 调用转账方法moneyService3.transferMoney();}
}

以上,就是采用【@Transactional】注解实现了事务控制,在实际开发过程中,我们都是采用注解进行事务控制,因为注解更加的简洁。

二、Spring中七种传播机制

2.1、什么是传播机制

事务的传播机制,是指:在多个方法之间调用的时候,其他方法应该采取哪种事务进行管理,是创建一个新事务运行呢,还是和前一个事务一起运行呢,还是以非事务的方式运行呢,我们把这种不同方法之间相互调用,事务在方法之间如何控制的过程称为:事务的传播机制。

简单理解:事务的传播机制就是事务在多个方法之间调用的时候,其他方法应该采取哪种方式来控制事务。

举个栗子:

  • 当前A类中有个A方法,这个A方法里面需要调用B类中的B方法完成某个功能。
  • 并且A、B方法是需要进行事务控制的,那么在A调用B方法的时候,B方法应该采取哪种方式运行呢???
  • 这里就需要考虑事务的传播机制了,如果A方法中存在事务控制,那么B方法可以创建新事务运行,也可以加入到A事务里面运行,或者B方法按照非事务的形式运行,B方法可以采取不同的传播机制来进行控制。

Spring框架中提供了七种传播机制,分别是如下所示:

  1. PROPAGATION_REQUIRED:默认传播行为。表示如果当前存在事务,则加入到当前事务运行;如果当前没有事务,则创建一个新事务运行。
  2. PROPAGATION_SUPPORTS:如果当前存在事务,则加入到当前事务运行;如果当前没有事务,则以非事务方式运行。
  3. PROPAGATION_MANDATORY:如果当前存在事务,则加入到当前事务运行;如果当前没有事务,则抛出异常。
  4. PROPAGATION_REQUIRES_NEW:无论当前是否存在事务,都是创建一个新事务运行。
  5. PROPAGATION_NOT_SUPPORTED:以非事务的方式运行;如果当前存在事务,则将当前事务挂起。
  6. PROPAGATION_NEVER:以非事务的方式运行;如果当前存在事务,则抛出异常。
  7. PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务中执行;如果当前没有事务,则按照REQUIRED属性执行。

2.2、七种传播机制

下面为了验证一下Spring中的事务传播机制,我们编写一些案例来看看不同事务的传播机制的使用。

案例准备:

  • 创建ServiceA类、ServiceB类。
  • ServiceA类中定义methodA方法,ServiceB类中定义methodB方法。
  • methodA方法开启事务,并且在方法中调用methodB方法,然后对methodB定义不同类型的传播机制。
  • 创建【ServiceA】类

@Component
public class ServiceA {@Autowiredprivate ServiceB serviceB;@Transactional // 开启事务public void methodA() {System.out.println("1、调用ServiceA类中的methodA方法......");System.out.println("2、调用serviceB.methodB()方法......");serviceB.methodB();}}
  • 创建【ServiceB】类
@Component
public class ServiceB {// 定义事务传播机制: 默认就是 REQUIRED@Transactional(propagation = Propagation.REQUIRED)public void methodB() {System.out.println("3、调用ServiceB类中的methodB方法......");}}
  • 创建【TestTx03】测试类
public class TestTx03 {public static void main(String[] args) {// 1、读取配置文件ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");// 2、获取对象ServiceA serviceA = context.getBean(ServiceA.class);// 调用转账方法serviceA.methodA();}
}

(1)PROPAGATION_REQUIRED

REQUIRED这种传播机制是:如果methodA方法有事务,那么methodB就会加入到methodA事务里面一起执行;如果methodA没有事务,那么methodB就会创建一个新事务执行。

那我们就这样测试一下,我们把methodA方法上面的事务注释掉,然后methodB中实现转账的功能,并且模拟出现错误,看最终数据库是否会被改变。

(2)PROPAGATION_SUPPORTS

SUPPORTS这种传播机制是:如果methodA方法有事务,那么methodB就会加入到methodA事务里面一起执行;如果methodA没有事务,那么methodB就会按照非事务的方式执行(也就是相等于普通方法执行)。

这个就很好测试了,我们就把methodB方法上面的传播机制修改一下,然后注释调用methodA方法的事务,这样查看运行结果,如果数据库的记录不正确,说明methodB是按照非事务运行的。

我们查看数据库的记录,发现数据库中两个用户的余额不正确了。

这就说明,methodB并没有事务,而是按照普通方法运行的。

(3)PROPAGATION_MANDATORY

MANDATORY这种传播机制是:如果methodA方法有事务,那么methodB就会加入到methodA事务里面一起执行;如果methodA没有事务,那么methodB就会抛出异常。

我们都讨论,在methodA没有事务的情况下,修改methodB方法的事务传播机制为【mandatory】,如下所示:

运行测试查看控制台是否抛出异常。

从控制台输出的异常日志可以看到,报错提示大致意思是:【对于manadatory类型的传播机制,没有找到存在的事务】。

No existing transaction found for transaction marked with propagation 'mandatory'.

(4)PROPAGATION_REQUIRES_NEW

REQUIRES_NEW这种传播机制是:无论methodA方法有没有事务,methodB都会创建有个新事务执行。

运行测试程序,可以发现数据库的记录没被修改,还是之前的状态,这就说明methodB有创建一个新事务运行。

(5)PROPAGATION_NOT_SUPPORTED

NOT_SUPPORTED​​​​​​​这种传播机制是:methodB按照非事务的方式运行,如果methodA存在事务,则将其挂起。

这个挂起怎么理解呢???挂起其实methodA方法里面如果有事务的话,那么methodA方法会被事务控制,但是methodA方法中调用了methodB方法,那么methodB是按照非事务运行的,此时methodA方法中的事务不会管理methodB方法,而是等methodB方法运行结束后,继续管理methodA方法的事务操作。

简单理解,就是methodA方法中的事务对methodB方法不生效,methodB方法里面发生异常后,methodB方法提交的SQL都会直接commit,而不会回滚。

上面,我们将methodA开启事务,methodB采用【NOT_SUPPORTED】传播机制,然后运行测试程序,查看结果。

查看数据库后,发现数据库里面的两条记录被修改了,这就说明methodB方法出现异常之前提交了一条SQL语句。

(6)PROPAGATION_NEVER

NEVER​​​​​​​这种传播机制是:methodB按照非事务的方式运行,如果methodA存在事务,则抛出异常。

将methodA开启事务,methodB修改为【NEVER】传播机制,查看运行结果。

可以看到,上面运行结果抛出异常,大致意思是:【对于never类型的传播机制,已经存在事务】。

Existing transaction found for transaction marked with propagation 'never'.

(7)PROPAGATION_NESTED

NESTED​​​​​​​​​​​​​​这种传播机制是:如果methodA方法存在事务,那么methodB就在嵌套事务里面执行;如果methodA没有事务,那么methodB就创建一个新事务执行。

这里我们对methodA和methodB都开启事务,并且methodB采用嵌套事务的传播机制,然后methodA方法里面有存钱的操作,methodB方法里面是转账的操作,程序运行时候,会按照嵌套事务规则运行,也就是methodB的事务里面出现异常,那么就会发生回滚操作,由于methodB发生异常,没有进行捕获,此时异常会一直向外抛出,异常抛出到methodA方法时候,此时methodA方法中的事务也会发生回滚。

我们可以捕获methodB方法,然后再次查看运行结果。

这次再运行测试程序,查看数据库可以发现,methodA中的执行成功。

methodA中存钱的操作成功了,当然这在实际里不能允许的。

2.3、Spring事务失效情况

Spring中虽然提供了事务来保证数据库的数据一致性,但是有时候Spring框架的事务也会失效,从而导致数据库数据错误,有如下五种情况可能会导致Spring事务失效。

(1)方法不是public修饰

Spring中事务是通过AOP实现的,而Spring所提供的AOP只能针对那些public修饰的方法进行拦截操作,所以如果方法不是public修饰的,那么也就无法对其进行AOP操作,对应的事务也就失效了。

(2)A方法通过this调用B方法

Spring事务是通过AOP实现的,而AOP是根据生成代理对象进行添加事务控制的,如果我们在一个类中通过【this】调用方法,那么是使用的普通对象,而不是代理对象,也就没法添加事务功能,所以事务就失效了。

这个具体的底层原理还是需要了解AOP,后面我学习到的时候,就继续写一篇关于事务是如何实现的文章。

(3)数据库不支持事务

这种情况没啥好说的,你数据库 都不支持事务,那么Spring肯定就没办法管理事务啦,也就事务失效了。

(4)对应的类没有被Spring管理

Spring的事务只能针对那些被IOC容器管理的类进行操作,如果某个类都没有被IOC容器所管理,那么Spring也就无法对其进行事务控制,所以事务也就失效了。

(5)异常被捕获未抛出

Spring中的事务只有在出现异常,并且被Spring的AOP异常通知捕获到的时候,事务才会进行回滚操作,否则事务将失效。

举个栗子:

  • A方法里面调用B方法,如果B方法中发生了异常,并且B方法通过【try...catch】进行了捕获,然后B方法也没有通过throw抛出异常,那么此时B方法中的事务就会认为执行成功,从而不会回滚。

综上,这篇文章结束了,主要介绍了如何使用注解进行事务控制,以及Spring中事务的七种传播机制、Spring事务失效的几个原因。

【源代码地址:https://gitee.com/zhuyoubin/ssm_code/tree/master/spring/spring-demo09】

【Spring笔记09】Spring中事务传播机制(注解方式)相关推荐

  1. spring上下文是什么意思_Java程序员只会CRUD连Spring事务传播机制都不懂?

    AQS到底有什么用?难道就真的只是为了面试吗? 当然不是说AQS没用,如果你不是做基础架构或者中间件开发,你很难感受到AQS的威力.当然,学习很多时候,需要的是正向反馈,学了太多造火箭的东西,面试完就 ...

  2. 原创 | CRUD更要知道的Spring事务传播机制

    来自:肥朝 AQS到底有什么用?难道就真的只是为了面试吗? 当然不是说AQS没用,如果你不是做基础架构或者中间件开发,你很难感受到AQS的威力.当然,学习很多时候,需要的是正向反馈,学了太多造火箭的东 ...

  3. Spring事务传播机制和隔离级别

    Spring有5种隔离级别,7种传播行为.这是面试常问的内容,也是代码中经常碰到的知识点.这些知识枯燥而且乏味,其中有些非常的绕.如果栽在这上面,就实在是太可惜了. @Transactional(is ...

  4. Spring事务与事务传播机制

    目录 1.事务的基本概念 2.Spring事务的实现 3.事务隔离级别 4.事务传播机制 1.事务的基本概念 关于事务的一些基础概念我已经在MYSQL中讲解过了,有不了解的可以移步至此篇文章: MyS ...

  5. Spring事务传播机制大白话(使用springboot,注解演示)

    1. 我对传播机制的理解 为什么需要传播机制? 因为事务之间可能存在相互调用,例如service业务层的方法存在相互调用,如果相互调用的方法都开启了事务(对应到springboot就是在方法上要添加@ ...

  6. java spring 事务传播_spring事务传播机制实例讲解

    天温习spring的事务处理机制,总结如下 对于SQL事务的概念以及ACID性质,可以参见我的另一篇博文 http://kingj.iteye.com/admin/blogs/1675011 spri ...

  7. Spring 事务传播机制 实例讲解

    事务传播机制 对于SQL事务的概念以及ACID性质,可以参见我的另一篇博文 http://kingj.iteye.com/admin/blogs/1675011 spring的管理的事务可以分为如下2 ...

  8. Spring事务传播机制以及事务嵌套

    Spring事务传播机制以及事务嵌套 Spring事务传播机制 事务嵌套场景 情景0: 场景1:不同类中,开启事务的方法调用没有开启事务的方法 场景2:不同类中,methodA方法嵌套methodB方 ...

  9. 【Spring事务】事务和事务传播机制

    事务 事务主要有三种操作: 开始事务 start transaction 提交事务 commit 回滚事务 rollback Spring 中事务的实现 Spring 中的事务操作分为两类: ⼿动操作 ...

  10. Spring的7种事务传播机制

    前言 什么是事务? 事务就是用户定义的一系列数据库操作,这些操作可以视为一个完成的逻辑处理工作单元,要么全部执行,要么全部不执行,是不可分割的工作单元.对数据库的增删改查操作 传播机制是什么? 当A调 ...

最新文章

  1. java字符串缓冲池分析
  2. Spark编程模型几大要素
  3. 使用CURL调用接口[*示例*]
  4. 【老孙随笔】项目经理要如何看待技术?
  5. php memcached windows,php memcached windows安装
  6. mysql+默认值+default_十六、MySQL 中数据类型的默认值 - default 约束-搜云库
  7. Discuz!X集群部署的系统方案和改造方式讨论
  8. 苹果cms安装 php映射,苹果cmsV10安装过程中的常见问题处理办法
  9. django的模板系统过滤器笔记
  10. 2014年电大计算机应用基础考,2017年电大计算机应用基础网考精彩试题与问题详解...
  11. 利用socket.io构建一个聊天室
  12. jsoncpp添加对象、数组与json对象的解析
  13. Chrome 插件PPAPI 开发(一)环境搭建
  14. 自建cdn搭建_在自己的服务器上搭建CDN加速服务的教程
  15. 原核DNA甲基化简述
  16. 攒齐智能产品组合,世界召唤四大超级力量
  17. tao.Opengl
  18. Python常见面试题汇总(根据面试总结)
  19. android 编辑9图片,Android .9.png图片的处理
  20. Latex在论文中输出微米和cm-1

热门文章

  1. win10 x64+VS2017社区版+OpenCV3.2.0安装
  2. 基于JAVA的旅游企业财务管理系统源码【包调试】
  3. matlab指派问题求法,matlab求解指派问题
  4. win7共享20人限制 清除_中控智慧考勤门禁_机器直连软件及清除管理员
  5. ogg格式怎样才能转换成MP3格式
  6. Python语言程序设计 第七周 文件和数据格式化
  7. IT行业毕业生投简历或面试技巧
  8. 485协议转换器的开发
  9. 操作无法完成因为其中的文件夹或文件已在另一个程序中打开
  10. 【转】西冷牛排,菲力牛排,眼肉牛排,雪花牛排,T骨牛排,沙朗牛排有什么不同?