spring事务

事务的概念:事务可以理解为操作DB的一个执行单元,最小粒度。

老生常谈的ACID

A(Automicty) 原子性,事务的操作一定是不可再被分隔的单位,执行结果,要么成功,要么失败

C(Consistency) 一致性,事务一定是使得数据库从一个一致性状态变为另一个一致性状态,通俗的说,就是数据一致性,例如多节点数据更新,广播分发这样的操作,事务操作完成后,一定是所有的节点数据都是一致的。广播节点,事务结束后,一定是全部从监听状态转到执行状态

一致性又分成强一致性,弱一致性,以及最终一致性,因为一些操作,做不到强一致性,例如主从数据节点的异步更新,这里就要求最终一致性。

I(Isolation) 隔离性,主要是处理高并发情况下,不同进程之间做到事务隔离,互不干扰,也是规避一些高并发问题,这里一般通过一些锁来做限制,比如数据库的乐观锁,悲观锁

  • READ_UNCOMMITTED
  • READ_COMMITTED
  • REPETABLE_READ
  • SERIALIZABLE

 有上往下,隔离级别越高,对脏读,重复度,幻读的处理就越严格,最后一个完全是事务串行化,性能最低,要谨慎使用。

贴一下脏读,幻读,非重复读的概念

脏读:

脏读发生在:当一个事务允许读取一个被其他事务改变但是未提交的状态时,这是因为并没有锁阻止读取,如上图,你看到第二个事务读取了一个并不一致的值,不一致的意思是,这个值是无效的,因为修改这个值的第一个事务已经回滚,也就是说,第一个事务修改了这个值,但是未提交确认,却被第二个事务读取,第一个事务又放弃修改,悔棋了,而第二个事务就得到一个脏数据。

非重复读:

反复读同一个数据却得到不同的结果,这是因为在反复几次读取的过程中,数据被修改了,这就导致我们使用了stale数据,这可以通过一个共享读锁来避免。这是隔离级别READ_COMMITTED会导致可重复读的原因。设置共享读锁也就是隔离级别提高到REPETABLE_READ。

幻读:

当第二个事务插入一行记录,而正好之前第一个事务查询了应该包含这个新纪录的数据,那么这个查询事务的结果里肯定没有包含这个刚刚新插入的数据,这时幻影读发生了                                

D(Durability) 持久性,事务一旦提交,对数据库数据的修改是永久生效的。

 

着重说一下spring的事务传播机制:

首先spring对事务的支持,其实也只是一种托管,真正做到回滚操作的,还是数据库本身,数据库连接本身提供commit,rollback这样的操作方法,然后基于自身binlog或者redo log来实现事务控制的,所以没有数据库的支持,spring也就无法支持事务。

 

spring事务的传播机制

REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED)

支持当前事务,如果没有事务会创建一个新的事务(这个是spring的默认传播机制,一般使用的都是这个)

SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS)

支持当前事务,如果没有事务的话以非事务方式执行

MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY)

支持当前事务,如果没有事务抛出异常

REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW) 创建一个新的事务并挂起当前事务
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED) 以非事务方式执行,如果当前存在事务则将当前事务挂起
NEVER(TransactionDefinition.PROPAGATION_NEVER) 以非事务方式进行,如果存在事务则抛出异常
NESTED(TransactionDefinition.PROPAGATION_NESTED)

如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

 

存在方法A methodA(), B methodB()两个方法

REQUIRED:

methodA()内部调用方法methodB()如果A开启了事务,B事务的传播机制为REQUIRED,则B发现自己已经在一个事务内,就不会再起新的事务,如果B发现自己没有在事务内,则会自己新起一个事务,保证一定有事务,出现异常就可以回滚。

REQUIRES_NEW:

依旧是methodA()内部调用方法methodB(),methodB()的传播机制为REQUIRES_NEW时,会自己创建一个新事务,并将A的事务挂起,等到B事务执行完成,才执行A事务,这是两个独立的事务,互不影响。也就是说,可能B发生了异常,做了回滚,A依旧正常提交了

其余的也都可以这样理解了

 

事务失效:

spring的生效,是通过spring初始化的时候,对需要事务托管的实例,做了代理,因此spring事务的失效,一般是springAop没有生效导致,例如没有catch到异常

try{}catch{}是常见的导致事务失效的原因

rollback异常与抛出的异常不匹配

数据库引擎不支持事务

如上A方法的传播机制如果为NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS)时候B方法也会事务失效

methodA()和methodB()方法在同一个类中Service中,A没有事务注解,方法B事务也会失效。当客户端Controller调用Service的时候,这时候是调用spring容器生产的Service,如果methodA存在事务,则Aop生效,当methodA()不存在事务注解的时候,内部调用方法methodB(),这时候直接this.methodB(),没有经过spring,导致springAop失效,也就不会调用代理类的invoke()方法,织入逻辑不生效,事务失效。

切面不生效原因如下图,动态代理最后对方法的调用,还是通过道理类调用了原生实例的方法,此时代理类前后置生效的方法就是m1,而m1内部调用m3方法,直接是内部调用了,没有走代理类。如果是调用代理类的m3方法,则织入逻辑生效,事务也就生效了。

 

 

 

spring的事务注解@Transactional只能放在public修饰的方法上才起作用,如果放在其他非public(private,protected)方法上,虽然不报错,但是事务不起作用

 

posted @   好好的一个居士  阅读(166)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示