数据库事务正确执行的四要素

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事务无效的源码分析相关推荐

  1. Spring配置详解,Spring配置元信息详解,Spring配置大全及源码分析

    文章目录 一.Spring都可以配置哪些元信息 二.Spring Bean 配置元信息 1.GenericBeanDefinition 2.RootBeanDefinition 3.Annotated ...

  2. CANOpen协议详解(一):CANfestival源码分析

    CANFestival-3源码详解一:重要结构体 有几点需要说明: 1.使用的是官网下载的canfestival-3源代码,下载的压缩包文件名是:Mongo-canfestival-3-asc-1a2 ...

  3. Spark详解(七):SparkContext源码分析以及整体作业提交流程

    1. SparkContext源码分析 在任何Spark程序中,必须要创建一个SparkContext,在SparkContext中,最主要的就是创建了TaskScheduler和DAGSchedul ...

  4. BMP180气压传感器详解与示例(STM32 附带源码)

    BMP180气压传感器详解与示例(STM32 附带源码) 简介 工作模式 校准数值 测试流程 第一步:微处理器读取校准数值 第二步:读取温度.气压初始值 第三步:计算温度.气压 第四步:计算海拔高度 ...

  5. Java API源码在哪里找_详解查看JAVA API及JAVA源码的方法

    在java的日常学习中,我们有时候会需要看java的api说明,或者是查看java的源码,使我们更好的了解java,接下来我就来说说如何查看java的api以及java源码 对于java的api,一般 ...

  6. vue 源码详解(零):Vue 源码流程图

    vue 源码详解(零):Vue 源码流程图 最近在研究 Vue 的源码, 整理博客, 结果想到的.看到的内容实在是太多了, 不知道从何写起, 故整理了一个大致的流程图,根据这个顺序进行一一整理. 为了 ...

  7. 阿里开源一站式分布式事务框架seata源码分析(AT模式下TM与RM分析)

    序言: 对于阿里开源分布式事务框架seata的详细了解可以参考官网,这里不会详细介绍.本章只会介绍seata中AT模式的源码分析(对阿seata有一定了解或者成功完成过demo). seata中一个事 ...

  8. mybatis第十话 - mybaits整个事务流程的源码分析

    1.故事前因 在分析mybatis源码时一直带的疑问,一直以为事务是在SqlSessionTemplate#SqlSessionInterceptor#invoke完成的,直到断点才发现并不简单! 在 ...

  9. 深入浅出Mybatis系列(四)---配置详解之typeAliases别名(mybatis源码篇)

    上篇文章<深入浅出Mybatis系列(三)---配置详解之properties与environments(mybatis源码篇)> 介绍了properties与environments, ...

最新文章

  1. 马斯克受罚离任董事长,默多克之子或将成接替者!
  2. SAP到SAP的升级 --- SAP行业新的金矿
  3. Leetcode:Search Insert Position
  4. idea无法搜索插件问题解决
  5. http协议的状态码
  6. 读Google是如何做测试的
  7. 明明白白你的Linux服务器——硬件篇
  8. cURL error 60: SSL certificate problem: unable to get local issuer certificate 解决思路
  9. 【c基础】之 文件及其操作
  10. Redis高可用基石--主从同步
  11. CoolUIViewAnimations
  12. L1-048 矩阵A乘以B-PAT团体程序设计天梯赛GPLT
  13. 基于QT开发的线性代数初学者的矩阵计算器设计
  14. DNK开发—Eclipse环境变量配置
  15. fluent p1模型_FLUENT模型选择
  16. 《Sony Vegas Pro 12标准教程》—— 2.6 添加背景音乐
  17. 为什么速度环给的是正反馈_什么是正反馈,负反馈,为什么要反馈?
  18. 小程序--微信拼团设计实现
  19. 有意思的互联网创业公司(Timehop/Redbeacon )
  20. OpenGL 入门 17:立方体贴图

热门文章

  1. android安卓手机变身无线网卡,实现“畅无线“电脑版”台式机笔记本通杀
  2. SnapdragonCamera源码分析(一)CameraActivity
  3. mysql limit 大数据_MySQL limit使用方法以及超大分页问题解决
  4. 裁员 or 缩招,AI 四起,你的工作还好吗?
  5. CocosCreator之控制游戏触点数量
  6. 徐小明20110913分析
  7. mybatisplus拦截器处理处理sql
  8. 3D全景虚拟旅游在旅游行业中具备哪些应用价值?
  9. c语言随机函数红包,抢红包算法(随机数)
  10. 用c语言写的新年搞笑祝福程序,新年微信红包搞笑句子_2020新年幽默微信红包祝福语...