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发生异常,而回滚了事务...https://blog.csdn.net/qq_41776884/article/details/81608777

5、事务的传播行为

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

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

你可能感兴趣的:(java,spring)