Spring 事务传播实践分析
事务管理是应用系统开发中必不可少的一部分。事物将若干的数据库操作作为一个整体控制,一起成功或一起失败。事物有以下特性
原子性:指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性:指事务前后数据的完整性必须保持一致。
隔离性:指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离。
持久性:指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,即时数据库发生故障也不应该对其有任何影响。
Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为编码式和声明式的两种方式。编程式事务指的是通过编码方式实现事务;声明式事务基于 AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。声明式事务有两种方式,一种是在配置文件(xml)中做相关的事务规则声明,另一种是基于@Transactional 注解的方式。注释配置是目前流行的使用方式,因此本文将着重介绍基于@Transactional 注解的事务管理。
@Transactional 注解的属性信息
| 属性名 | 说明 |
| name | 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。 |
| propagation | 事务的传播行为,默认值为 REQUIRED。 |
| isolation | 事务的隔离度,默认值采用 DEFAULT。 |
| timeout | 事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。 |
| read-only | 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。 |
| rollback-for | 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。 |
| no-rollback- for | 抛出 no-rollback-for 指定的异常类型,不回滚事务。 |
事物传播行为:
- REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
- SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
- MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
- REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
- NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
- NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与REQUIRED类似的操作。拥有多个可以回滚的保存点,内部回滚不会对外部事务产生影响。只对DataSourceTransactionManager有效
事物隔离级别
- DEFAULT--使用后端数据库默认的隔离级别(Spring中的选择项)
- READ_UNCOMMITED--允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读
- READ_COMMITTED--允许在并发事务已经提交后读取。可防止脏读,但幻读和不可重复读仍可发生(锁定正在读取的行)
- REPEATABLE_READ--对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生(锁定所读取的所有行)
- SERIALIZABLE--完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的 (锁表)
其中,MySQL默认采用REPEATABLE_READ隔离级别;Oracle默认采用READ_COMMITTED隔离级别
依据事务的隔离级别将会有三种情况发生。
脏读:一个事务会读进还没有被另一个事务提交的数据,所以你会看到一些最后被另一个事务回滚掉的数据。
不可重复读:一个事务读进一条记录,另一个事务更改了这条记录并提交完毕,这时候第一个事务再次读这条记录时,它已经改变了。
幻影读:一个事务用Where子句来检索一个表的数据,另一个事务插入一条新的记录,并且符合Where条件,这样,第一个事务用同一个where条件来检索数据后,就会多出一条记录。
我们首先先实验事务的传播
注意:所有的测试都是调用method1
public void method1() {
TicketEntity ticketEntity = new TicketEntity();
ticketEntity.setF_apply_service("测试");
ticketEntityMapper.insertSelective(ticketEntity);
method2(ticketEntity.getF_ticket_id());
throw new RuntimeException("事务需要回滚");
}
@Transactional(propagation = Propagation.REQUIRED)
public void method2(Integer id) {
TicketEntity ticketEntity = new TicketEntity();
ticketEntity.setF_apply_service("测试2");
ticketEntityMapper.insertSelective(ticketEntity);
}
结果:都不回滚
public void method1() {
TicketEntity ticketEntity = new TicketEntity();
ticketEntity.setF_apply_service("测试");
ticketEntityMapper.insertSelective(ticketEntity);
method2();
// throw new RuntimeException("事务需要回滚");
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void method2() {
TicketEntity ticketEntity = new TicketEntity();
ticketEntity.setF_apply_service("测试2");
ticketEntityMapper.insertSelective(ticketEntity);
throw new RuntimeException("事务需要回滚");
}
结果:都不回滚
// @Transactional(propagation = Propagation.REQUIRED)
public void method1() {
TicketEntity ticketEntity = new TicketEntity();
ticketEntity.setF_apply_service("测试");
ticketEntityMapper.insertSelective(ticketEntity);
method2();
// throw new RuntimeException("事务需要回滚"); } @Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void method2() {
TicketEntity ticketEntity = new TicketEntity();
ticketEntity.setF_apply_service("测试2");
ticketEntityMapper.insertSelective(ticketEntity);
throw new RuntimeException("事务需要回滚");
}
结果:都不回滚
@Transactional(propagation = Propagation.REQUIRED)
public void method1() {
TicketEntity ticketEntity = new TicketEntity();
ticketEntity.setF_apply_service("测试");
ticketEntityMapper.insertSelective(ticketEntity);
method2();
// throw new RuntimeException("事务需要回滚"); } @Override
// @Transactional(propagation = Propagation.REQUIRED)
public void method2() {
TicketEntity ticketEntity = new TicketEntity();
ticketEntity.setF_apply_service("测试2");
ticketEntityMapper.insertSelective(ticketEntity);
throw new RuntimeException("事务需要回滚");
}
结果:回滚
@Transactional(propagation = Propagation.REQUIRED)
public void method1() { TicketEntity ticketEntity = new TicketEntity();
ticketEntity.setF_apply_service("测试");
ticketEntityMapper.insertSelective(ticketEntity);
method2();
// throw new RuntimeException("事务需要回滚1");
} @Override
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
public void method2() {
TicketEntity ticketEntity = new TicketEntity();
ticketEntity.setF_apply_service("测试2");
ticketEntityMapper.insertSelective(ticketEntity);
// throw new RuntimeException("事务需要回滚2");
}
结果:不回滚,如果直接调用method2,则会报错,说明自调用时注解时没有效果的。
在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理,这会造成自调用问题。若同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,有@Transactional 注解的方法的事务被忽略,不会发生回滚。
这是由于使用 Spring AOP 代理造成的。为解决这两个问题,使用 AspectJ 取代 Spring AOP 代理。
Spring 事务传播实践分析的更多相关文章
- spring事务传播实现源码分析
转载. https://blog.csdn.net/qpfjalzm123/article/details/83717367 本文只是对spring事务传播实现的流程进行简单的分析,如有不对之处请指出 ...
- spring 事务传播行为实例分析
Spring事务传播行为: spring特有的事务传播行为,spring支持7种事务传播行为,确定客户端和被调用端的事务边界(说得通俗一点就是多个具有事务控制的service的相互调用时所形成的复杂的 ...
- spring事务传播特性实验(2):PROPAGATION_REQUIRED实验结果与分析
本文延续上一文章(spring事务传播特性实验(1):数据准备),在已经准备好环境的情况下,做如下的实验,以验证spring传播特性,加深对spring传播特性的理解. 本次主要验证PROPAGATI ...
- spring事务传播机制实例讲解
http://kingj.iteye.com/blog/1680350 spring事务传播机制实例讲解 博客分类: spring java历险 天温习spring的事务处理机制,总结 ...
- Spring事务传播机制
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播,即协调已经有事务标识的方法之间的发生调用时的事务 ...
- Spring事务传播机制和数据库隔离级别
Spring事务传播机制和数据库隔离级别 转载 2010年06月26日 10:52:00 标签: spring / 数据库 / exception / token / transactions / s ...
- Spring事务传播属性介绍(二).mandatory、not_supported、never、supports
Required.Required_New传播属性分析传送门:https://www.cnblogs.com/lvbinbin2yujie/p/10259897.html Nested传播属性分析传送 ...
- Spring事务传播属性介绍(三).Nested
Required.Required_New传播属性分析传送门:https://www.cnblogs.com/lvbinbin2yujie/p/10259897.html Mandatory.Neve ...
- Spring事务传播属性介绍(一).required 和 reuqires_new
Mandatory.Never.Not_Support传播属性分析传送门:https://www.cnblogs.com/lvbinbin2yujie/p/10260030.html Nested传播 ...
随机推荐
- 4月23日 db 命令操作 和表操作
1内容回顾: # 补充的知识点 # server端肯定是确定下来的 # mysql的客户端 # mysql.exe 直接在命令行就可以运行的 (学习阶段用) # navicat等可视化的客户端,是第三 ...
- ubuntu18.04时区设置
1.运行命令 sudo tzselect 2.选择大区 选择亚洲Asia,继续选择中国China,最后选择北京Beijing 3.建立软链 ln -sf /usr/share/zoneinfo/Asi ...
- Linux---一级/二级目录以及位置目录名/指令
home目录:普通用户登录进来以后的初始位置(会在home目录下创建一个登录名相同的目录例如 / home / 用户名),如果是超级用户则就是 在根目录 /下的 root目录(也就是 /root) ...
- flume接收http请求,并将数据写到kafka
flume接收http请求,并将数据写到kafka,spark消费kafka的数据.是数据采集的经典框架. 直接上flume的配置: source : http channel : file sink ...
- PHP安装+使用
curl -s http://php-osx.liip.ch/install.sh | bash -s 5.4 ...... Extracting usr/local/php5-5 ...
- 数据结构C语言版-队列
#include <stdlib.h> #include <stdio.h> #include <iostream> using namespace std; ty ...
- C# 服务开发
1. 执行权限 对于一般服务项目,需要使用管理员权限打开Visual Studio.右击项目 -> 属性 -> 安全性,如下图: 勾选红框中部分,项目中会增加文件 app.mainfest ...
- 微软develop apps在QQ上部分功能的实现
最近我对微软的develop apps的文档进行了简读,在感叹UWP在支持服务上的全面的同时,我不禁在在常用的APP上对于这些功能支持进行了部分的寻找对应.而我进行功能对照的,就是平时很常用的一款手机 ...
- Navicat连接oracle库报错的处理方法
1.oracle客户端版本:11.2.0 64X navicat premium客户端:12.0. 64X 2.安装后,打开连接,输入连接信息,点击保存,提示oracle library is no ...
- eclipse项目无故报错,markers信息为An error occurred while filtering resources
eclipse项目无故报错,markers信息为An error occurred while filtering resources 描述:eclipse项目和resource文件上有红色的叉,其m ...