Spring自学日志03-事务
一、事务的基本概念
1.1、事务的ACID属性
提到事务,不可避免需要涉及到事务的ACID属性:
- 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
- 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
- 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
- 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。
我们将严格遵循ACID属性的事务称为刚性事务。与之相对,期望最终一致性,在事务执行的中间状态允许暂时不遵循ACID属性的事务称为柔性事务,可参考传统事务与柔性事务,柔性事务的使用涉及到分布式事务方案,可以后续扩展,这里我们先将注意集中在事务实现原理上。
1.2、事务的隔离级别
根据SQL92标准,MySQL的InnoDB引擎提供四种隔离级别(即ACID中的I):
- 读未提交(READ UNCOMMITTED)
- 读已提交(READ COMMITTED)
- 可重复读(REPEATABLE READ)
- 串行化(SERIALIZABLE)
InnoDB默认的隔离级别是REPEATABLE READ
,其可避免脏读和不可重复读,但不能避免幻读,需要指出的是,InnoDB引擎的多版本并发控制机制(MVCC)并没有完全避免幻读,关于该问题以及隔离级别说明,可参考MySQL的InnoDB的幻读问题。
1.3、事务的传播机制
Spring针对方法嵌套调用时事务的创建行为定义了七种事务传播机制,分别是
- PROPAGATION_REQUIRED,如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置,也是Spring事务默认的传播机制
- PROPAGATION_REQUIRES_NEW,创建一个新事务,如果存在当前事务,则挂起该事务
- PROPAGATION_SUPPORT,当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行
- PROPAGATION_NOT_SUPPORTED,始终以非事务方式执行,如果当前存在事务,则挂起当前事务
- PROPAGATION_MANDATORY,当前存在事务,则加入当前事务,如果当前事务不存在,则抛出异常
- PROPAGATION_NEVER,不使用事务,如果当前事务存在,则抛出异常
- PROPAGATION_NESTED,如果当前事务存在,则在嵌套事务中执行,否则REQUIRED的操作一样(开启一个事务)
基本上从字面意思就能知道每种传播机制各自的行为表现,Spring默认的事务传播机制是PROPAGATION_REQUIRED
,即如果当前存在事务,则使用当前事务,否则创建新的事务。
1.4、通过注解开启事务
在 Spring 项目中,我们可以直接使用注解 @Transactional 来标识一个类或方法支持事务。
//可以在类上
@Service
@Transactional
public class Target implements IUserService {
............
}
//也可以在类的方法上
@Service
public class Target implements IUserService {
@Override
@Transactional
public void Transaction(UserEntity entity1, UserEntity entity2) {
.........
}
}
该注解建议在类上加在方法上加会有隐患,一个没有加@Transactional(MothodA)注解的方法,去调用一个加了@Transactional的方法(MothodB),不会产生事务
public class Target {
//没有加@Transactional(MothodA)
public void MothodA(UserEntity entity1, UserEntity entity2) {
MothodB(entity1, entity2);
}
//加了@Transactional的方法(MothodB)
@Transactional
public void MothodB(UserEntity entity1, UserEntity entity2) {
.........
}
}
要知道原因首先了解@Transactional的原理。调用@Transactional方法,如何产生事务?
其实Spring的@Transactional,跟Spring AOP一样,都是利用了动态代理。
我们写了一个类,里面写了一个加了@Transactional注解的方法,这原本平淡无奇,什么用也没有,就像这样:
关键在于,Spring在检查到@Transactional注解之后,给这个对象生成了一个代理对象proxy:
代理对象的methodB,会先开启事务(beginTransaction),然后再去执行原先对象target的methodB,如果抛异常,则回滚(rollBack),如果一切顺利,则提交(commit)。
而最后注入Spring容器的,也正是这个带有事务逻辑的代理对象。所以我们调用methodB时会产生事务。
现在,我们写了一个新方法,methodA,里头去调用methodB:
从上面的分析,可以知道,我们最终拿到的,是代理对象。
那么代理对象的methodA是长什么样子的呢?长这样:
由于methodA没有加@Transactional注解,所以代理对象里面,直接就是target.methodA(),直接调用了原来对象的methodA。
这下就很清晰了,代理对象的methodA,去调用原来对象的methodA,原来对象的methodA,再去调用原来对象的methodB,而原来对象的methodB,是不具有事务的。事务只存在于代理对象的methodB. 所以整个方法也就没有事务了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!