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)方法上,虽然不报错,但是事务不起作用
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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代理 了,记录一下