【Spring笔记09】Spring中事务传播机制(注解方式)
这篇文章,主要介绍如何使用注解进行事务控制,以及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框架中提供了七种传播机制,分别是如下所示:
- PROPAGATION_REQUIRED:默认传播行为。表示如果当前存在事务,则加入到当前事务运行;如果当前没有事务,则创建一个新事务运行。
- PROPAGATION_SUPPORTS:如果当前存在事务,则加入到当前事务运行;如果当前没有事务,则以非事务方式运行。
- PROPAGATION_MANDATORY:如果当前存在事务,则加入到当前事务运行;如果当前没有事务,则抛出异常。
- PROPAGATION_REQUIRES_NEW:无论当前是否存在事务,都是创建一个新事务运行。
- PROPAGATION_NOT_SUPPORTED:以非事务的方式运行;如果当前存在事务,则将当前事务挂起。
- PROPAGATION_NEVER:以非事务的方式运行;如果当前存在事务,则抛出异常。
- 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中事务传播机制(注解方式)相关推荐
- spring上下文是什么意思_Java程序员只会CRUD连Spring事务传播机制都不懂?
AQS到底有什么用?难道就真的只是为了面试吗? 当然不是说AQS没用,如果你不是做基础架构或者中间件开发,你很难感受到AQS的威力.当然,学习很多时候,需要的是正向反馈,学了太多造火箭的东西,面试完就 ...
- 原创 | CRUD更要知道的Spring事务传播机制
来自:肥朝 AQS到底有什么用?难道就真的只是为了面试吗? 当然不是说AQS没用,如果你不是做基础架构或者中间件开发,你很难感受到AQS的威力.当然,学习很多时候,需要的是正向反馈,学了太多造火箭的东 ...
- Spring事务传播机制和隔离级别
Spring有5种隔离级别,7种传播行为.这是面试常问的内容,也是代码中经常碰到的知识点.这些知识枯燥而且乏味,其中有些非常的绕.如果栽在这上面,就实在是太可惜了. @Transactional(is ...
- Spring事务与事务传播机制
目录 1.事务的基本概念 2.Spring事务的实现 3.事务隔离级别 4.事务传播机制 1.事务的基本概念 关于事务的一些基础概念我已经在MYSQL中讲解过了,有不了解的可以移步至此篇文章: MyS ...
- Spring事务传播机制大白话(使用springboot,注解演示)
1. 我对传播机制的理解 为什么需要传播机制? 因为事务之间可能存在相互调用,例如service业务层的方法存在相互调用,如果相互调用的方法都开启了事务(对应到springboot就是在方法上要添加@ ...
- java spring 事务传播_spring事务传播机制实例讲解
天温习spring的事务处理机制,总结如下 对于SQL事务的概念以及ACID性质,可以参见我的另一篇博文 http://kingj.iteye.com/admin/blogs/1675011 spri ...
- Spring 事务传播机制 实例讲解
事务传播机制 对于SQL事务的概念以及ACID性质,可以参见我的另一篇博文 http://kingj.iteye.com/admin/blogs/1675011 spring的管理的事务可以分为如下2 ...
- Spring事务传播机制以及事务嵌套
Spring事务传播机制以及事务嵌套 Spring事务传播机制 事务嵌套场景 情景0: 场景1:不同类中,开启事务的方法调用没有开启事务的方法 场景2:不同类中,methodA方法嵌套methodB方 ...
- 【Spring事务】事务和事务传播机制
事务 事务主要有三种操作: 开始事务 start transaction 提交事务 commit 回滚事务 rollback Spring 中事务的实现 Spring 中的事务操作分为两类: ⼿动操作 ...
- Spring的7种事务传播机制
前言 什么是事务? 事务就是用户定义的一系列数据库操作,这些操作可以视为一个完成的逻辑处理工作单元,要么全部执行,要么全部不执行,是不可分割的工作单元.对数据库的增删改查操作 传播机制是什么? 当A调 ...
最新文章
- java字符串缓冲池分析
- Spark编程模型几大要素
- 使用CURL调用接口[*示例*]
- 【老孙随笔】项目经理要如何看待技术?
- php memcached windows,php memcached windows安装
- mysql+默认值+default_十六、MySQL 中数据类型的默认值 - default 约束-搜云库
- Discuz!X集群部署的系统方案和改造方式讨论
- 苹果cms安装 php映射,苹果cmsV10安装过程中的常见问题处理办法
- django的模板系统过滤器笔记
- 2014年电大计算机应用基础考,2017年电大计算机应用基础网考精彩试题与问题详解...
- 利用socket.io构建一个聊天室
- jsoncpp添加对象、数组与json对象的解析
- Chrome 插件PPAPI 开发(一)环境搭建
- 自建cdn搭建_在自己的服务器上搭建CDN加速服务的教程
- 原核DNA甲基化简述
- 攒齐智能产品组合,世界召唤四大超级力量
- tao.Opengl
- Python常见面试题汇总(根据面试总结)
- android 编辑9图片,Android .9.png图片的处理
- Latex在论文中输出微米和cm-1
热门文章
- win10 x64+VS2017社区版+OpenCV3.2.0安装
- 基于JAVA的旅游企业财务管理系统源码【包调试】
- matlab指派问题求法,matlab求解指派问题
- win7共享20人限制 清除_中控智慧考勤门禁_机器直连软件及清除管理员
- ogg格式怎样才能转换成MP3格式
- Python语言程序设计 第七周 文件和数据格式化
- IT行业毕业生投简历或面试技巧
- 485协议转换器的开发
- 操作无法完成因为其中的文件夹或文件已在另一个程序中打开
- 【转】西冷牛排,菲力牛排,眼肉牛排,雪花牛排,T骨牛排,沙朗牛排有什么不同?