• Spring Boot 事务详解


    【自己学习事务做的笔记,方便记忆和复习】

    1、事务是为了解决数据安全问题而存在的。

    最经典的例子就是银行转账问题,A账户给B账户转账100元,A账户扣除100元后由于不可抗力因素导致程序中断,B账户没有收到那100元,A账户那100元凭空消失,肯定是不行的。A扣款和B收款操作要么同时成功,要么同时失败,这个时候就需要引入事务操作。

    2、事务的四个特性:

    • **原子性:**一个事务是一个不可分割的工作单位。
    • **一致性:**事务必须是使数据库从一个一致性状态变到另一个一致性状态,一致性与原子性是密切相关的。
    • **隔离性:**一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
    • **持久性:**一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

    3、事务管理方式

    spring支持编程式事务管理声明式事务管理两种方式。

    编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。

    声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于**@Transactional注解**的方式),便可以将事务规则应用到业务逻辑中。

    @Transactional注解:

    它是声明式事务管理编程中使用的注解,放在接口实现类或接口实现方法上,并且只对public方法才起作用。只读的接口不需要事务管理,防止影响系统性能。

    @Transactional 实质是使用了 JDBC 的事务来进行事务控制的,实现原理:

    1. 事务开始时,通过AOP机制,生成一个代理connection对象,并将其放入 DataSource 实例的某个与 DataSourceTransactionManager 相关的某处容器中。在接下来的整个事务中,客户代码都应该使用该 connection 连接数据库,执行所有数据库命令。[不使用该 connection 连接数据库执行的数据库命令,在本事务回滚的时候得不到回滚](物理连接 connection 逻辑上新建一个会话session;DataSource 与 TransactionManager 配置相同的数据源)

    2. 事务结束时,回滚在第1步骤中得到的代理 connection 对象上执行的数据库命令,然后关闭该代理 connection 对象。(事务结束后,回滚操作不会对已执行完毕的SQL操作命令起作用)

    4、事务的隔离级别

    当多个线程都开启事务操作数据库中的数据时,数据库系统要能进行隔离操作,以保证各个线程获取数据的准确性。隔离级别越高,数据库的并发性能就越差。

    第一种隔离级别:Read uncommitted(读未提交)

    在该隔离级别下,所有事务都可以看到其它未提交事务的执行结果。即在该级别下,事务的修改即便没有提交,对其他事务也都是可见的,可能出现脏读、不可重复读、幻读。

    第二种隔离级别:Read committed(读提交)

    该隔离级别满足了隔离的简单定义,一个事务只能看见已经提交事务所做的改变。这是Oracle数据库默认的事务隔离级别。避免了脏读,可能出现不可重复读、幻读。

    第三种隔离级别:Repeatable read(可重复读取)

    可以确保同一个事务在多次读取同样的数据时,返回同样的结果。这是MySQL数据库默认的事务隔离级别。这样避免了不可重复读和脏读,但是有时可能会出现幻读。

    第四种隔离级别:Serializable(可序化)

    它通过强制事务排序,使事务一个一个的进行,事务之间不可能再存在相互冲突,从而解决幻读问题。

    脏读、不可重复读、幻读:

    1、脏读

    脏读就是指当A事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,B事务也访问这个数据,然后使用了这个数据。这时候如果事务A回滚,那么B事务读到的数据是不被承认的。

    2、不可重复读(重点在修改,体现在值不同)

    指在A事务内,多次读同一数据。在A事务还没有结束时,B事务也访问该同一数据。那么,在A事务中的两次读数据之间,由于B事务的修改,那么A事务两次读到的的数据可能是不一样的。这样就发生了在A事务内两次读到的数据是不一样的。

    3、幻读(重点在增加或删除,体现在记录数不同)

    是指当事务不是独立执行时发生的一种现象,例如A事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,B事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作A事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

    具体可看↓数据库事务隔离级别(脏读、幻读、不可重复读)_qq_41776884的博客-CSDN博客_脏读幻读不可重复读一、脏读、幻读和不可重复读一、脏读、不可重复读、幻读1、脏读:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。例如:张三的工资为5000,事务A中把他的工资改为8000,但事务A尚未提交。与此同时,事务B正在读取张三的工资,读取到张三的工资为8000。随后,事务A发生异常,而回滚了事务…[这里是图片001]https://blog.csdn.net/qq_41776884/article/details/81608777

    5、事务的传播行为

    事务的传播行为是针对嵌套事务而言的。具体可见↓【十六】Spring Boot之事务(事务传播机制、嵌套事务、事务隔离机制详解)_jy02268879的博客-CSDN博客_springboot 事务嵌套一、事务传播机制:事务的传播行为是针对嵌套事务而言。示例:@Transactional(propagation = Propagation.REQUIRED)2.1.1REQUIREDspring默认的事务传播行为就是它。支持事务。如果业务方法执行时已经在一个事务中,则加入当前事务,否则重新开启一个事务。外层事务提交了,内层才会提交。内/外只要有报错,他俩会一起回滚。…[这里是图片002]https://blog.csdn.net/jy02268879/article/details/84322459

    1.REQUIRED

    @Transactional(propagation = Propagation.REQUIRED)
    spring中的默认事务传播行为就是它。如果业务方法执行时已经在一个事务中,则加入当前事务,

    否则重新开启一个事务。外层事务提交了,内层才会提交。内/外只要有报错,他俩会一起回滚。

    只要内层方法报错抛出异常,即使外层有try-catch,该事务也会回滚。

    因为内外层方法在同一个事务中,内层只要抛出了异常,这个事务就会被设置成rollback-only,即使外层try-catch内层的异常,该事务也会回滚。

    2.REQUIRES_NEW

    @Transactional(propagation = Propagation.REQUIRES_NEW)

    支持事务。每次都是创建一个新事物,如果当前已经在事务中了,会挂起当前事务。内层事务结束,内层就提交了,不用等着外层一起提交。

    外层报错回滚,不影响内层。内层报错回滚,外层try-catch内层的异常,外层不会回滚。

    内层报错回滚,然后又会抛出异常,外层如果没有捕获处理内层抛出来的这个异常,外层还是会回滚的。

    3.NESTED

    @Transactional(propagation = Propagation.NESTED)

    支持事务。如果当前事务存在,那么在嵌套的事务中执行,内层事务结束,要等着外层一起提交。如果当前事务不存在,则表现跟REQUIRED一样。

    这个直接说,如果外层报错回滚,内层也会跟着回滚。

    如果只是内层回滚,不影响外层。这个内层回滚不影响外层的特性是有前提的,否则内外都回滚。

    内层是NESTED模式下,外层要try-catch内层的异常,外层才不会回滚。而内层是REQUIRED模式的话,即使外层try-catch内层异常,外层同样会回滚的。

    4.SUPPORTS

    @Transactional(propagation = Propagation.SUPPORTS)

    支持事务。当前有事务就支持使用当前事务,若当前不存在事务,以非事务的方式执行。内层事务结束,要等着外层一起提交。

    5.MANDATORY

    @Transactional(propagation = Propagation.MANDATORY)

    支持事务,如果业务方法执行时已经在一个事务中,则加入当前事务。否则抛出异常。内层事务结束,要等着外层一起提交。

    6.NOT_SUPPORTED

    @Transactional(propagation = Propagation.NOT_SUPPORTED)

    不支持事务,以非事务的方式执行,若当前存在事务,则把当前事务挂起,等方法执行完毕后,事务恢复进行。

    若A是事务执行,B(NOT_SUPPORTED非事务执行)B在A尚未提交前再操作同一条记录,会产生死锁,A、B不可操作同一条记录。

    7.NEVER

    @Transactional(propagation = Propagation.NEVER)

    不支持事务。如果当前已经在一个事务中了,抛出异常。

    先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦

  • 相关阅读:
    【JAVA基础】【查漏补缺】07 - 集合Collection和单列集合List
    Sentinel实现熔断与限流
    【微机原理|课程报告】双机并口通信课设报告
    【Nacos无压力源码领读】(二) 集成 LoadBalancer 与 OpenFeign
    MongoDB,入门看这一篇足矣!
    【Linux】C文件系统详解(三)——如何理解缓冲区以及自主封装一个文件接口
    FFmpeg中的时间戳与时间基
    FutureTask-详解(二)-ThreadPollExecutor-并发编程(Java)
    qnx sh: rm: Arg list too long
    物联网Lora模块从入门到精通(四)对某些端口的初始化
  • 原文地址:https://blog.csdn.net/jiey0407/article/details/126114081