@Transactional的用法详解及Transactional事务无效的源码分析
数据库事务正确执行的四要素
1.原子性
事务是不可分割的最小的工作单元,事务内的操作要么全做,要么全不做,不能只做一部分。
2.一致性
事务执行前数据库的数据按照逻辑处于正确的状态,事务执行后数据库的数据按照逻辑也处于正确状态。如果事务执行前后不是逻辑应该的正确状态,那么数据是不一致的。
3.隔离性
在并发事务的条件下,事务之间互不影响,即一个事务的内部操作对其他事务不影响,需要设置事务的隔离级别来指定隔离性。
4.持久性
事务一旦执行成功,对数据库数据的修改是永久的,不会因为各种异常导致数据不一致或丢失。
数据库事务并发执行会遇到的问题
脏读:一个事务A读取了另一个事务B没有提交的数据,如果事务B回滚了,那么事务A读取的数据不正确。
丢失更新:两个事务同时更新一条数据,导致最后一个事物的更新覆盖了前面事务的更新值。一般由于没加锁的原因造成。
不可重复读:同一个事物内两次读取数据结果不同。例如事务A读取数据,其中事务B对数据做修改,造成同一个事务中事务A再次读取数据结果与上一次不同。
幻读:一个事务A读取了另一个事务B新insert的数据。同一个事务中事务A后一次读取要比前一次多了数据。
SQL的4个等级的事务隔离级别
级别 | 性能 |
未提交读(READ UNCOMMITTED ) | 最低隔离级别,一个事务能读取到别的事务未提交的更新数据,很不安全,可能出现丢失更新、脏读、不可重复读、幻读 |
提交读(READ COMMITTED) | 一个事务能读取到别的事务提交的更新数据,不能看到未提交的更新数据,不会出现丢失更新、脏读,但可能出现不可重复读、幻读; |
可重复读(REPEATABLE READ) | 保证同一事务中先后执行的多次查询将返回同一结果,不受其他事务影响,不可能出现丢失更新、脏读、不可重复读,但可能出现幻读; |
序列化(SERIALIZABLE) | 最高隔离级别,不允许事务并发执行,而必须串行化执行,最安全,不可能出现更新、脏读、不可重复读、幻读,但是效率最低。 |
事务的隔离级别越高,数据库事务并发执行的性能越差,MySQL支持四种事务等级,默认事务的隔离级别是REPEATABLE READ。Oracle数据库支持READ COMMITTED和SERIALIZABLE这两种事务隔离级别,Oracle数据库不支持脏读。Oracle默认的数据库隔离级别是READ COMMITTED。Sqlserver默认的数据库隔离级别是READ COMMITTED。
@Transactional参数注解详解
- propagation:事务传播行为参数
@Transactional(propagation=Propagation.REQUIRED) :如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED) :容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW) :不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY) :必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER) :必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) :如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
- timeout:事务超时设置
@Transactional(timeout=50) //默认是50秒
- isolation :事务隔离级别
@Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读)
@Transactional(isolation = Isolation.SERIALIZABLE):串行化
一般项目中不会配置isolation,isolation 为默认值,也就是所连接数据库的默认事务隔离级别。
Isolation isolation() default Isolation.DEFAULT;/***事务隔离级别为连接数据库的默认隔离级别*/
DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),
- readOnly: 设置为true,表示当前事务是只读事务,设置false,表示当前事务为非只读事务。
- rollbackFor: 设置需要回滚的异常类,只有这种异常类及其子类抛出异常后,事务才会回滚。
//抛出单个异常类
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
//抛出多个异常类
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = {Throwable.class,RuntimeException.class})
- noRollbackFor: 设置不需要回滚的异常类,当事务抛出这种异常的时候,事务不回滚。
//指定多个异常类
@Transactional(propagation = Propagation.REQUIRED, noRollbackFor = {Throwable.class,RuntimeException.class})
//指定单一异常类
@Transactional(propagation = Propagation.REQUIRED, noRollbackFor=Throwable.class)
- rollbackForClassName:设置需要回滚的异常类名称,只有抛出这种异常的时候,事务回滚。
//指定多个异常类
@Transactional(propagation = Propagation.REQUIRED, rollbackForClassName = {"Throwable","RuntimeException"})
//指定一个异常类@Transactional(propagation = Propagation.REQUIRED, rollbackForClassName = "Throwable")
- noRollbackForClassName:设置不需要回滚的异常类名称,抛出这种异常的时候,事务不回滚。
@Transactional(propagation = Propagation.REQUIRED, noRollbackForClassName = {"Throwable","RuntimeException"})@Transactional(propagation = Propagation.REQUIRED, noRollbackForClassName = "Throwable")
@Transactional 的Spring事务配置
<bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean>
<!-- 使用annotation定义事务 --><tx:annotation-driven transaction-manager="transactionManager"/>
@Transactional事务失效的例子介绍
1.@Transactional注解在非public方法上时,事务回滚不起作用
@Service
public class StudentService {private static final Logger logger = LoggerFactory.getLogger(SelfEmployedInfoImportService.class);@Autowiredprivate ScoreMapper scoreMapper;@Autowiredprivate StudentMapper studentMapper;@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Throwable.class)//在非public方法上用@Transactionalvoid save(ScoreVo scoreVo, StudentVo studentVo){try{studentMapper.insert(studentVo);scoreMapper.insert(scoreVo);}catch(Exception e){logger.error("错误",e);throw e;}}
}@Service
public class StudentServiceTest {@Autowiredprivate StudentService studentService;//同一个包中引用非public方法public void save(ScoreVo scoreVo, StudentVo studentVo){studentService.save(scoreVo,studentVo);}
}//测试用例
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-config.xml"})
public class MainToken {@Autowiredprivate StudentService studentService;@Autowiredprivate StudentServiceTest studentServiceTest;@Testpublic void saveStudentTest(){ScoreVo scoreVo=new ScoreVo();scoreVo.setScore(new BigDecimal("100"));scoreVo.setScSubject("语文");StudentVo student=new StudentVo();student.setStName("lily");student.setStNumber("1");studentServiceTest.save(scoreVo,student);}
}
事务测试效果:student表有数据,score表没有数据,事务没有起作用。
2.在类内部调用@Transactional事务失效
@Service
public class StudentService {private static final Logger logger = LoggerFactory.getLogger(SelfEmployedInfoImportService.class);@Autowiredprivate ScoreMapper scoreMapper;@Autowiredprivate StudentMapper studentMapper;@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Throwable.class)//方法save和方法saveLevel是同一个类StudentService的内部方法public void save(ScoreVo scoreVo, StudentVo studentVo){try{studentMapper.insert(studentVo);scoreMapper.insert(scoreVo);}catch(Exception e){logger.error("错误",e);throw e;}}//方法save和方法saveLevel是同一个类StudentService的内部方法public void saveLevel(ScoreVo scoreVo, StudentVo studentVo){//同一个类内的方法调用另一个方法save(scoreVo,studentVo);}}@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-config.xml"})
public class MainToken {@Autowiredprivate StudentService studentService;@Autowiredprivate StudentServiceTest studentServiceTest;@Testpublic void saveTest(){ScoreVo scoreVo=new ScoreVo();scoreVo.setScore(new BigDecimal("100"));scoreVo.setScSubject("语文");StudentVo student=new StudentVo();student.setStName("lily");student.setStNumber("1");studentService.saveLevel(scoreVo,student);}}
测试结果:student表有数据,score表没有数据,事务没有起作用。
如果改为内部方法用代理去访问,((类名) AopContext.currentProxy()),则事务起作用。代码如下
@Service
public class StudentService {private static final Logger logger = LoggerFactory.getLogger(SelfEmployedInfoImportService.class);@Autowiredprivate ScoreMapper scoreMapper;@Autowiredprivate StudentMapper studentMapper;@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Throwable.class)//方法save和方法saveLevel是同一个类StudentService的内部方法public void save(ScoreVo scoreVo, StudentVo studentVo){try{studentMapper.insert(studentVo);scoreMapper.insert(scoreVo);}catch(Exception e){logger.error("错误",e);throw e;}}//方法save和方法saveLevel是同一个类StudentService的内部方法public void saveLevel(ScoreVo scoreVo, StudentVo studentVo){//同一个类内的方法调用另一个方法((StudentService) AopContext.currentProxy()).save(scoreVo,studentVo);}}@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-config.xml"})
public class MainToken {@Autowiredprivate StudentService studentService;@Autowiredprivate StudentServiceTest studentServiceTest;@Testpublic void saveTest(){ScoreVo scoreVo=new ScoreVo();scoreVo.setScore(new BigDecimal("100"));scoreVo.setScSubject("语文");StudentVo student=new StudentVo();student.setStName("lily");student.setStNumber("1");studentService.saveLevel(scoreVo,student);}}
测试结果:student表无数据,score表无数据,事务有效。
3.事务有异常没抛出,异常被捕捉了,导致事务失效。
@Service
public class StudentService {private static final Logger logger = LoggerFactory.getLogger(SelfEmployedInfoImportService.class);@Autowiredprivate ScoreMapper scoreMapper;@Autowiredprivate StudentMapper studentMapper;@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Throwable.class)public void save(ScoreVo scoreVo, StudentVo studentVo){try{studentMapper.insert(studentVo);scoreMapper.insert(scoreVo);}catch(Exception e){logger.error("错误",e);}}public void saveLevel(ScoreVo scoreVo, StudentVo studentVo){((StudentService) AopContext.currentProxy()).save(scoreVo,studentVo);}}@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-config.xml"})
public class MainToken {@Autowiredprivate StudentService studentService;@Autowiredprivate StudentServiceTest studentServiceTest;@Testpublic void saveTest(){ScoreVo scoreVo=new ScoreVo();scoreVo.setScore(new BigDecimal("100"));scoreVo.setScSubject("语文");StudentVo student=new StudentVo();student.setStName("lily");student.setStNumber("1");studentService.saveLevel(scoreVo,student);}}
测试结果:student表有数据,score表无数据,事务失效。
@Transactional事务失效的源码分析
见链接:https://blog.csdn.net/qq_20597727/article/details/84900994
https://blog.csdn.net/qq_20597727/article/details/84868035
@Transactional的用法详解及Transactional事务无效的源码分析相关推荐
- Spring配置详解,Spring配置元信息详解,Spring配置大全及源码分析
文章目录 一.Spring都可以配置哪些元信息 二.Spring Bean 配置元信息 1.GenericBeanDefinition 2.RootBeanDefinition 3.Annotated ...
- CANOpen协议详解(一):CANfestival源码分析
CANFestival-3源码详解一:重要结构体 有几点需要说明: 1.使用的是官网下载的canfestival-3源代码,下载的压缩包文件名是:Mongo-canfestival-3-asc-1a2 ...
- Spark详解(七):SparkContext源码分析以及整体作业提交流程
1. SparkContext源码分析 在任何Spark程序中,必须要创建一个SparkContext,在SparkContext中,最主要的就是创建了TaskScheduler和DAGSchedul ...
- BMP180气压传感器详解与示例(STM32 附带源码)
BMP180气压传感器详解与示例(STM32 附带源码) 简介 工作模式 校准数值 测试流程 第一步:微处理器读取校准数值 第二步:读取温度.气压初始值 第三步:计算温度.气压 第四步:计算海拔高度 ...
- Java API源码在哪里找_详解查看JAVA API及JAVA源码的方法
在java的日常学习中,我们有时候会需要看java的api说明,或者是查看java的源码,使我们更好的了解java,接下来我就来说说如何查看java的api以及java源码 对于java的api,一般 ...
- vue 源码详解(零):Vue 源码流程图
vue 源码详解(零):Vue 源码流程图 最近在研究 Vue 的源码, 整理博客, 结果想到的.看到的内容实在是太多了, 不知道从何写起, 故整理了一个大致的流程图,根据这个顺序进行一一整理. 为了 ...
- 阿里开源一站式分布式事务框架seata源码分析(AT模式下TM与RM分析)
序言: 对于阿里开源分布式事务框架seata的详细了解可以参考官网,这里不会详细介绍.本章只会介绍seata中AT模式的源码分析(对阿seata有一定了解或者成功完成过demo). seata中一个事 ...
- mybatis第十话 - mybaits整个事务流程的源码分析
1.故事前因 在分析mybatis源码时一直带的疑问,一直以为事务是在SqlSessionTemplate#SqlSessionInterceptor#invoke完成的,直到断点才发现并不简单! 在 ...
- 深入浅出Mybatis系列(四)---配置详解之typeAliases别名(mybatis源码篇)
上篇文章<深入浅出Mybatis系列(三)---配置详解之properties与environments(mybatis源码篇)> 介绍了properties与environments, ...
最新文章
- 马斯克受罚离任董事长,默多克之子或将成接替者!
- SAP到SAP的升级 --- SAP行业新的金矿
- Leetcode:Search Insert Position
- idea无法搜索插件问题解决
- http协议的状态码
- 读Google是如何做测试的
- 明明白白你的Linux服务器——硬件篇
- cURL error 60: SSL certificate problem: unable to get local issuer certificate 解决思路
- 【c基础】之 文件及其操作
- Redis高可用基石--主从同步
- CoolUIViewAnimations
- L1-048 矩阵A乘以B-PAT团体程序设计天梯赛GPLT
- 基于QT开发的线性代数初学者的矩阵计算器设计
- DNK开发—Eclipse环境变量配置
- fluent p1模型_FLUENT模型选择
- 《Sony Vegas Pro 12标准教程》—— 2.6 添加背景音乐
- 为什么速度环给的是正反馈_什么是正反馈,负反馈,为什么要反馈?
- 小程序--微信拼团设计实现
- 有意思的互联网创业公司(Timehop/Redbeacon )
- OpenGL 入门 17:立方体贴图
热门文章
- android安卓手机变身无线网卡,实现“畅无线“电脑版”台式机笔记本通杀
- SnapdragonCamera源码分析(一)CameraActivity
- mysql limit 大数据_MySQL limit使用方法以及超大分页问题解决
- 裁员 or 缩招,AI 四起,你的工作还好吗?
- CocosCreator之控制游戏触点数量
- 徐小明20110913分析
- mybatisplus拦截器处理处理sql
- 3D全景虚拟旅游在旅游行业中具备哪些应用价值?
- c语言随机函数红包,抢红包算法(随机数)
- 用c语言写的新年搞笑祝福程序,新年微信红包搞笑句子_2020新年幽默微信红包祝福语...