👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中... 博客更新速度++
👍 欢迎点赞、收藏、关注,跟上我的更新节奏
📚欢迎订阅专栏,专栏名《# 在6月写Spring相关文章还算春天吗》
什么是Spring事务?
Spring事务是Spring框架提供的一种数据库事务管理机制,其核心目标是确保一组数据库操作要么全部成功提交,要么全部失败回滚,从而保证事务的ACID。
Spring事务基于底层数据库的事务能力(如MySQL的InnoDB引擎),并通过抽象和封装,使得开发者可以更方便地控制事务的边界和行为。
这里给忘记事务的UU们,回忆一波,数据库事务的ACID
事务的四大特性(ACID)
- 原子性(Atomicity) :事务中的操作要么全部成功,要么全部失败回滚。
- 一致性(Consistency) :事务执行前后,数据库的完整性约束(如外键、唯一性)必须保持一致。
- 隔离性(Isolation) :多个事务并发执行时,彼此隔离,避免相互干扰。
- 持久性(Durability) :事务提交后,修改永久保存到数据库中,即使系统崩溃也不丢失。
使用Spring事务
Spring的事务,分为编程式和声式事务。
简单来说就是,编程式事务是手动写,声式事务是用注释。
声明式事务
使用 @Transactional 注解 + Spring AOP 动态代理来实现,Spring 在启动时会扫描所有带有 @Transactional 的类或方法,并为其创建一个代理对象。当调用这些方法时,Spring 会在方法执行前后自动开启、提交或回滚事务。
typescript
@Transactional
public void transferMoney() {
// 数据库操作
}
// 等价于
proxy.transferMoney() {
try {
transactionManager.begin();
target.transferMoney();
transactionManager.commit();
} catch (Exception e) {
transactionManager.rollback();
throw e;
}
}
那让我们看看这个@Transactional
注解。
我们可以看到它可以修饰在类(ElementType.TYPE)和方法(ElementType.METHOD)上。
less
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {...
当修饰在类上时:
- 该类中所有 public 方法都会继承事务行为。
- 所有方法都默认具有相同的事务属性(如传播行为、隔离级别等)。
- 如果某个方法需要不同的事务配置,可以在该方法上单独添加
@Transactional
并覆盖类级别的设置。
less
@Service
@Transactional
public class OrderService {
public void placeOrder() {
// 数据库操作 1
// 数据库操作 2
}
public void cancelOrder() {
// 数据库操作
}
}
修饰在方法上时:
- 只对标注了
@Transactional
的方法进行事务增强。 - 更细粒度地控制每个方法的事务行为(比如只读、超时、回滚规则等)。
- 推荐用于大多数业务场景,更清晰可控。
typescript
@Service
public class OrderService {
@Transactional
public void placeOrder() {
// 操作数据库,事务管理生效
}
public void sendNotification() {
// 不在事务中执行
}
}
好奇的你一定会聪明,那@Transactional,可以设置啥呢?诶嘿嘿,那可多了:

字段名 | 作用描述 | 可选值/示例 | 默认值 |
---|---|---|---|
propagation |
事务传播行为:定义与现有事务的关系 | REQUIRED , REQUIRES_NEW , SUPPORTS , NOT_SUPPORTED , MANDATORY , NEVER , NESTED |
REQUIRED (存在则加入,不存在则新建) |
isolation |
事务隔离级别:控制并发访问数据的可见性 | READ_UNCOMMITTED , READ_COMMITTED , REPEATABLE_READ , SERIALIZABLE |
DEFAULT (通常为数据库默认级别) |
timeout |
超时时间(秒) :事务执行超过此时长自动回滚 | 正整数,例如 timeout = 30 |
-1 (无超时限制) |
readOnly |
是否只读:提示数据库优化 (非强制约束) | true (只读), false (可写) |
false (可写) |
rollbackFor |
触发回滚的异常类:指定哪些异常(含检查异常)应回滚事务 | 异常类数组,例如 rollbackFor = {SQLException.class} |
仅 RuntimeException 和 Error 回滚 |
noRollbackFor |
不触发回滚的异常类 :指定哪些 RuntimeException 不触发回滚 |
异常类数组,例如 noRollbackFor = {IllegalArgumentException.class} |
所有 RuntimeException 均回滚 |
transactionManager |
指定事务管理器:多数据源时选择特定事务管理器 Bean | 事务管理器 Bean 名称,例如 transactionManager = "orderTxMgr" |
"" (使用默认事务管理器) |
编程式事务
使用transactionTemplate
进行事务管理,TransactionTemplate
是Spring提供的一个工具类,用于以编程方式管理事务。它简化了事务代码的编写,避免了冗余的try-catch块。
typescript
@Autowired
private TransactionTemplate transactionTemplate;
public void performTransactionalOperation() {
transactionTemplate.execute(status -> {
// 执行数据库操作
return null; // 返回值可以是任意结果对象
});
}
你是否会和我一样好奇,这个返回值是干嘛的?当然是获取事务的执行结果了,比如我们执行了一条update语句,我们可以返回更新成功的行数,如这样:
ini
Integer rowsAffected = transactionTemplate.execute(status -> {
return studentRepo.updateNameById("李四", 1L);
});
PlatformTransactionManager
,这个更加的原始,通过手动调用其方法(如getTransaction
和commit/rollback
)直接控制事务
java
@Autowired
private PlatformTransactionManager transactionManager;
public void performManualTransaction() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
// 执行数据库操作
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
}
🙋主播,主播,那上面的@Transactional
,可以设置一些事物相关的属性(传播行为、隔离级...) 这个手动挡,我们要怎么赋值勒?诶哟我,这个非常之简单:
如果你使用的是transactionTemplate
,那你只要使用它的set方法就行了
csharp
public void query() {
transactionTemplate.setIsolationLevel(TransactionTemplate.ISOLATION_READ_COMMITTED);
transactionTemplate.execute(status -> {
Long cnt = studentRepo.selectCount(new LambdaQueryWrapper<StudentPO>().eq(StudentPO::getName, "z1"));
return cnt;
});
}

如果你使用的是PlatformTransactionManager
那你可以对DefaultTransactionDefinition
进行set赋值。
java
public class SomeService {
private final PlatformTransactionManager transactionManager;
public SomeService(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public void someMethod() {
// 定义事务属性
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED); // 设置隔离级别
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); // 设置传播行为
def.setTimeout(30); // 设置超时时间(秒)
def.setReadOnly(false); // 是否只读
// 开启事务
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 执行业务逻辑
// ...
// 提交事务
transactionManager.commit(status);
} catch (Exception ex) {
// 回滚事务
transactionManager.rollback(status);
throw ex;
}
}
}
我们可以康康DefaultTransactionDefinition
可以设置什么:

你是否会好奇?这个setIsolationLevel
和setIsolationLevelName
有什么区别?
ini
// 静态写法(推荐日常开发使用)
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
// 动态写法(适合配置中心或参数传入)
String isolationStr = "ISOLATION_REPEATABLE_READ";
def.setIsolationLevelName(isolationStr)
事务传播行为(Propagation)
经过了上面的学习,聪明的你,一定学了Spring事务的基本使用,至少😄,可以在异常的时候,对数据进行回滚了,但是,好学的你一定会想要更多更多,下面我们来说说----Spring事务传播行为。
事务的传播行为(Transaction Propagation Behavior)是指:在多个方法相互调用时,事务应该如何传递和协调。简单来说,它决定了被调用方法是否要继续当前事务、开启新事务、还是不使用事务。
Spring中定义了7个传播行为

定义事务方法如何与现有事务交互:
- REQUIRED(默认):如果当前存在事务,则加入,否则新建事务。
- REQUIRES_NEW:无论当前是否存在事务,都新建事务。
- NESTED:在当前事务中嵌套子事务,子事务可独立回滚。
- SUPPORTS:如果当前存在事务,则加入;否则以非事务方式执行。
- NOT_SUPPORTED:以非事务方式执行,挂起当前事务(如果存在)。
- MANDATORY:强制要求当前存在事务,否则抛出异常。
- NEVER:强制要求当前不存在事务,否则抛出异常。
REQUIRED(默认)
如果当前已经存在 一个事务,方法就加入这个现有事务,成为它的一部分。
如果当前不存在 事务,方法就自己开启一个新事务。
下面的代码示例,主播想实验,methodB是一个事务,读不到methodA事务还没提交的数据。
java
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
studentRepo.insert(new StudentPO(null, "a")); // 插入名字叫a的学生
((StudentService) AopContext.currentProxy()).methodB();
}
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
Long aStuCnt = studentRepo.selectCount(new LambdaQueryWrapper<StudentPO>().eq(StudentPO::getName, "a"));
log.info("aStuCnt:{}", aStuCnt); // 因为和methodA在同一个事务,这里会输出1
studentRepo.insert(new StudentPO(null, "b"));
throw new RuntimeException("error");
}
还有就是,如果methodB出现异常,就算在methodA异常被捕获,插入的学生a数据也会被回滚(因为methodA和methodB用的是一个同一个事务)
java
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
studentRepo.insert(new StudentPO(null, "a"));
try {
((StudentService) AopContext.currentProxy()).methodB();
} catch (Exception e) {
// 就算这里,捕获了异常,且不继续抛出,还是会导致之前插入的学生a数据回滚
}
}
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
Long aStuCnt = studentRepo.selectCount(new LambdaQueryWrapper<StudentPO>().eq(StudentPO::getName, "a"));
log.info("aStuCnt:{}", aStuCnt);
studentRepo.insert(new StudentPO(null, "b"));
throw new RuntimeException("error");
}
REQUIRES_NEW
REQUIRES_NEW
表示:无论当前是否存在事务,该方法都必须在独立的新事务中运行。
无外部事务时: 直接启动并运行于一个新事务中。
有外部事务时:
- 挂起当前事务: 立即挂起(suspended)已存在的外部事务。
- 创建新事务: 启动一个全新且独立的事务。
- 方法执行: 方法的所有操作在新事务上下文中执行。
- 新事务结束:
-
- 成功(无异常): 提交新事务,更改永久生效。
- 失败(有异常): 回滚新事务,撤销其所有更改。
- 恢复外部事务: 新事务结束后(无论提交或回滚),恢复之前挂起的外部事务,其后续操作不受新事务结果影响。
java
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
studentRepo.insert(new StudentPO(null, "a")); // 插入名字叫a的学生
((StudentService) AopContext.currentProxy()).methodB();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
Long aStuCnt = studentRepo.selectCount(new LambdaQueryWrapper<StudentPO>().eq(StudentPO::getName, "a"));
log.info("aStuCnt:{}", aStuCnt); // 因为开启了新的事务,隔离级别是RR(默认),a事务插入的数据是查不到的,这里会是0
studentRepo.insert(new StudentPO(null, "b"));
throw new RuntimeException("error");
}
如果methodB出现异常,异常被捕获,插入的学生b数据会被回滚,但是学生a数据的插入不会被回滚(因为methodA和methodB用的是2个事务)
java
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodA() {
studentRepo.insert(new StudentPO(null, "a"));
try {
((StudentService) AopContext.currentProxy()).methodB();
} catch (Exception e) {
// methodA和methodB是2个事务,methodB异常被捕获,没有继续抛出异常,插入的学生a数据不会被回滚
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
Long aStuCnt = studentRepo.selectCount(new LambdaQueryWrapper<StudentPO>().eq(StudentPO::getName, "a"));
log.info("aStuCnt:{}", aStuCnt); // 0
studentRepo.insert(new StudentPO(null, "b")); // 插入学生b数据会被回滚
throw new RuntimeException("error");
}
NESTED
NESTED
表示:在现有事务中嵌套一个可独立回滚的子事务(基于数据库 Savepoint)。
无外部事务时: 行为等同于 REQUIRED
(开启新事务)。
有外部事务时:
- 创建保存点: 在当前事务内创建数据库保存点(Savepoint),标记子事务起点。
- 执行方法: 方法在嵌套子事务上下文中执行。⚠注意:这里的NESTED 传播行为中的"子事务"和"父事务"(外部事务)使用的是同一个物理数据库事务
- 子事务结束:
- 成功(无异常): 子事务操作暂存,等待外部事务最终提交。
java
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
studentRepo.insert(new StudentPO(null, "a")); // 插入名字叫a的学生
((StudentService) AopContext.currentProxy()).methodB();
}
@Transactional(propagation = Propagation.NESTED)
public void methodB() {
Long aStuCnt = studentRepo.selectCount(new LambdaQueryWrapper<StudentPO>().eq(StudentPO::getName, "a"));
log.info("aStuCnt:{}", aStuCnt); // 因为和methodA是同一个事务,所以这里是1
studentRepo.insert(new StudentPO(null, "b"));
throw new RuntimeException("error");
}
- 失败(异常被捕获): 仅回滚子事务操作 (回滚到保存点),外部事务及其之前操作不受影响。
java
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
studentRepo.insert(new StudentPO(null, "a")); // 插入学生a数据
try {
((StudentService) AopContext.currentProxy()).methodB();
} catch (Exception e) {
// 异常被捕获但不抛出
// 之前的插入学生a的数据不会被回滚
}
}
@Transactional(propagation = Propagation.NESTED)
public void methodB() {
Long aStuCnt = studentRepo.selectCount(new LambdaQueryWrapper<StudentPO>().eq(StudentPO::getName, "a"));
log.info("aStuCnt:{}", aStuCnt); // 1
studentRepo.insert(new StudentPO(null, "b")); // 插入学生b数据
throw new RuntimeException("error"); // 抛出异常,学生b数据回滚
}
- 失败(异常未捕获或外部事务失败): 整个外部事务 (含所有嵌套操作)全部回滚。
java
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
studentRepo.insert(new StudentPO(null, "a")); // 插入学生a数据
((StudentService) AopContext.currentProxy()).methodB(); // 调用methodB异常,学生a数据回滚
}
@Transactional(propagation = Propagation.NESTED)
public void methodB() {
Long aStuCnt = studentRepo.selectCount(new LambdaQueryWrapper<StudentPO>().eq(StudentPO::getName, "a"));
log.info("aStuCnt:{}", aStuCnt); // 1
studentRepo.insert(new StudentPO(null, "b")); // 插入学生b数据
throw new RuntimeException("error"); // 抛出异常,学生b数据回滚
}
- 外部事务提交: 成功时,所有嵌套子事务的操作最终生效。
SUPPORTS
SUPPORTS
: 支持当前事务,若无事务则以非事务方式执行。
存在事务时: 方法加入 当前已存在的事务,成为其一部分。注意下面的代码例子,methodA
的传播行为是Propagation.REQUIRED
,它在没有事务的时候,会开启一个事务,让传播行为Propagation.SUPPORTS
的methodB
加入这个事务。
java
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
studentRepo.insert(new StudentPO(null, "a")); // 插入学生a数据,此时事务还没有提交
((StudentService) AopContext.currentProxy()).methodB(); // 异常回滚学生a、b数据
}
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
Long aStuCnt = studentRepo.selectCount(new LambdaQueryWrapper<StudentPO>().eq(StudentPO::getName, "a"));
log.info("aStuCnt:{}", aStuCnt); // 1,因为是和methodA同一个事务,可以读到methodA没有提交的数据
studentRepo.insert(new StudentPO(null, "b")); // 插入学生b数据
throw new RuntimeException("error");
}
不存在事务时: 方法在无事务上下文 中执行,操作直接提交(依赖数据库的 autocommit
)。注意下面的代码例子,两个方法事务的传播行为都是Propagation.SUPPORTS
java
@Transactional(propagation = Propagation.SUPPORTS)
public void methodA() {
studentRepo.insert(new StudentPO(null, "a")); // 事务1:插入学生a数据,插入成功后自动提交事务
((StudentService) AopContext.currentProxy()).methodB();
}
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
Long aStuCnt = studentRepo.selectCount(new LambdaQueryWrapper<StudentPO>().eq(StudentPO::getName, "a"));
log.info("aStuCnt:{}", aStuCnt); // 因为是新的事务,这里能读到事务1的数据,所以这里是1
studentRepo.insert(new StudentPO(null, "b")); // 事务2:插入学生b数据,插入成功后自动提交事务
throw new RuntimeException("error");
}
NOT_SUPPORTED
NOT_SUPPORTED
: 不支持当前事务;始终以非事务方式执行,并暂停现有事务。
存在事务时:
- 挂起当前事务: Spring 会立即挂起(suspend) 任何已存在的事务。
- 非事务执行: 方法在无任何事务上下文中执行。
- 恢复事务: 方法执行完成后,恢复(resume) 之前挂起的事务。
不存在事务时:
- 方法直接在无事务上下文中执行。
异常影响:
- 无论调用时是否存在事务,方法都在非事务模式下运行。
- 方法内部抛出的任何异常:
-
- 不会导致事务回滚(因为它不在事务中运行)。
- 已执行的 SQL 操作无法回滚 (依赖数据库
autocommit
,通常已立即生效)。 - 只影响方法自身执行流程。
- 如果外部事务被挂起,其状态不受该方法异常或成功的影响。恢复后,外部事务继续执行,仿佛该方法不存在于其事务上下文中。
一个代码直接结束😎,methodA在事务中执行,methodB非事务执行,所以下面的代码,插入的学生a数据会被回滚,
java
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
studentRepo.insert(new StudentPO(null, "a")); // 插入学生数据a
((StudentService) AopContext.currentProxy()).methodB();
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void methodB() {
Long aStuCnt = studentRepo.selectCount(new LambdaQueryWrapper<StudentPO>().eq(StudentPO::getName, "a"));
log.info("aStuCnt:{}", aStuCnt); // 0,因为methodA事务还没有提交(隔离级别默认RR)
studentRepo.insert(new StudentPO(null, "b")); // 插入学生数据b
throw new RuntimeException("error"); // 抛出异常,学生数据b不会被回滚
}
MANDATORY
MANDATORY 表示:必须存在一个事务,否则抛异常
它不会自己开启新事务,而是要求调用者必须已经开启了事务,即:只有在有事务上下文的前提下,该方法才能正常执行。所以下面方法,要是直接调用,会报错:
java
@Transactional(propagation = MANDATORY)
public void query() {
Long cnt = studentRepo.selectCount(new LambdaQueryWrapper<StudentPO>().eq(StudentPO::getName, "z1"));
}
报错内容
less
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:362) ~[spring-tx-6.0.9.jar:6.0.9]
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:601) ~[spring-tx-6.0.9.jar:6.0.9]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:385) ~[spring-tx-6.0.9.jar:6.0.9]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-6.0.9.jar:6.0.9]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.9.jar:6.0.9]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-6.0.9.jar:6.0.9]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:702) ~[spring-aop-6.0.9.jar:6.0.9]
at top.flzjkl.service.StudentService$$SpringCGLIB$$0.query(<generated>) ~[classes/:na]
NEVER
NEVER
: 绝对不支持当前事务;如果存在事务,则直接抛出异常。
存在事务时:
- 立即抛出异常: Spring 会检测到当前存在活动事务,并立即抛出
IllegalTransactionStateException
,阻止方法执行。
不存在事务时:
- 方法在无事务上下文中正常执行。
- 每条 SQL 通常立即生效(依赖数据库
autocommit
)。
异常影响:
- 有事务场景: 方法根本不会执行,由抛出的异常中断流程。
- 无事务场景: 方法内部抛出的任何异常
-
- 不会导致事务回滚(因为无事务)。
- 已执行的 SQL 操作无法回滚。
- 只影响方法自身执行流程。
java
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
studentRepo.insert(new StudentPO(null, "a"));
((StudentService) AopContext.currentProxy()).methodB(); // 这里直接就是异常,methodB的传播行为,不允许有事务
}
@Transactional(propagation = Propagation.NEVER)
public void methodB() {
Long aStuCnt = studentRepo.selectCount(new LambdaQueryWrapper<StudentPO>().eq(StudentPO::getName, "a"));
log.info("aStuCnt:{}", aStuCnt);
studentRepo.insert(new StudentPO(null, "b"));
throw new RuntimeException("error");
}
输出异常
less
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
at org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(AbstractPlatformTransactionManager.java:413) ~[spring-tx-6.0.9.jar:6.0.9]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:352) ~[spring-tx-6.0.9.jar:6.0.9]
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:601) ~[spring-tx-6.0.9.jar:6.0.9]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:385) ~[spring-tx-6.0.9.jar:6.0.9]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-6.0.9.jar:6.0.9]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.9.jar:6.0.9]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750) ~[spring-aop-6.0.9.jar:6.0.9]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:702) ~[spring-aop-6.0.9.jar:6.0.9]
at top.flzjkl.service.StudentService$$SpringCGLIB$$0.methodB(<generated>) ~[classes/:na]
at top.flzjkl.service.StudentService.methodA(StudentService.java:57) ~[classes/:na]
事务隔离级别(Isolation)
没啥好说的,和MySQL的隔离级别一个意思

控制事务之间的可见性,解决并发问题(脏读、不可重复读、幻读):
DEFAULT
:使用数据库默认级别(如MySQL默认是REPEATABLE_READ
)。READ_UNCOMMITTED
:允许读取未提交的数据(可能脏读)。READ_COMMITTED
:只能读取已提交的数据(解决脏读)。REPEATABLE_READ
:确保同一事务多次读取结果一致(解决不可重复读)。SERIALIZABLE
:完全串行化执行(解决幻读,性能最低)。
事务失效的场景
一般来说,声明式事务,比较容易出现事务失效,而声明式事务的原理是AOP。
好了,废话不多说,直接开始到事务失效的一些场景。
- 方法自调用(最常见也是最经典的陷阱):
- 当一个类内部的方法A(非事务方法)调用同一个类内部的事务方法B时,由于调用发生在目标对象内部,绕过了Spring创建的代理对象,导致事务拦截器(AOP Advisor)无法介入,事务注解
@Transactional
失效。 - 原因: Spring的事务管理是通过代理对象实现的。自调用发生在目标对象内部,没有经过代理。
- 事务方法定义非
public
:
- Spring AOP 默认使用基于接口的JDK动态代理或基于类的CGLIB代理。对于非
public
方法:
-
- JDK代理: 只能代理接口中的方法,非
public
方法不会被代理。 - CGLIB代理: 虽然可以代理类,但无法代理
private
方法。protected
或包级私有的方法在CGLIB代理下理论上可以被代理,但Spring的事务基础架构(AbstractFallbackTransactionAttributeSource
)默认只查找 ****public
****方法上的 ****@Transactional
****注解! 因此,非public
方法上的@Transactional
会被忽略。
- JDK代理: 只能代理接口中的方法,非
- 结论: 确保事务方法是
public
的。
- 异常类型不正确或被"吞掉":
-
- 默认回滚异常:
@Transactional
默认只在遇到运行时异常 (RuntimeException
及其子类)和错误 (Error
)时才回滚。遇到检查型异常 (Exception
的子类,非RuntimeException
)不会回滚。 - 自定义回滚异常: 可以使用
@Transactional(rollbackFor = MyCheckedException.class)
指定需要回滚的检查型异常。 - 异常被捕获未抛出: 如果在事务方法内部捕获了异常(尤其是默认会触发回滚的
RuntimeException
或Error
)并且没有重新抛出,那么事务管理器就感知不到异常的发生,事务会正常提交。这是导致事务"看似生效但未回滚"的常见原因。 - 抛出非回滚异常: 如果抛出的异常类型不在默认回滚列表或
rollbackFor
指定的列表中,事务也不会回滚。
- 默认回滚异常:
- 数据库引擎不支持事务:
- 如果你使用的数据库存储引擎本身不支持事务(例如MySQL的MyISAM),那么无论Spring如何配置,事务都不可能生效。必须使用支持事务的引擎(如MySQL的InnoDB)。
- 未被Spring管理:
- 包含
@Transactional
注解的Bean必须是由Spring容器创建和管理的。自己new
出来的对象,其上的@Transactional
注解无效。
- 传播行为设置不当:
- 虽然传播行为本身是特性而非Bug,但如果理解错误可能导致事务行为不符合预期(例如,期望开启新事务但实际使用了
SUPPORTS
或MANDATORY
导致没有事务)。
- 多数据源下事务管理器配置错误:
- 使用多个数据源时,必须为每个数据源配置对应的事务管理器(
DataSourceTransactionManager
)。在@Transactional
注解或TransactionTemplate
中需要明确指定使用哪个事务管理器(通过value
或transactionManager
属性)。如果未正确指定,可能导致事务作用于错误的数据源或根本不生效。