事务失效几种场景
时间: 2025-03-10 16:10:22 浏览: 43
### Spring Boot 中事务失效的常见场景及其原因
#### 非公共方法上使用 `@Transactional` 注解
当在一个非 `public` 方法上应用 `@Transactional` 注解时,该注解不会起作用。这是因为 Spring 的事务管理依赖于 AOP 动态代理机制来拦截被标记的方法调用并为其提供事务支持。然而,默认情况下,只有对外暴露的服务接口中的公开方法才会创建代理实例[^1]。
```java
@Service
class MyService {
@Transactional
protected void nonPublicMethod() { /* ... */ } // 事务不起效
@Transactional
public void publicMethod() { /* ... */ } // 正常工作
}
```
#### 同一 Bean 内部自我调用
如果在同一个类内的某个带有 `@Transactional` 的方法尝试去调用另一个同样位于此类下的无此标注或者有不同传播行为设置的方法,则后者将不在前者开启的事物上下文中运行。因为内部调用并不会触发外部由AOP所建立起来的那个环绕通知链路,所以也就失去了原本预期得到的那种跨多个操作共享同一笔交易的能力[^4]。
```java
@Service
class SelfCallingBean {
@Transactional
public void outerCall(){
innerNonTx(); // 不会在outerCall的事务中执行
}
private void innerNonTx(){/*...*/}
}
```
为了使这种情形下的事物能够正常运作,可以通过让服务层组件实现自定义接口的方式来自我引用自己;或者是利用 `ApplicationContextAware` 接口获取当前应用程序环境进而间接访问到目标bean对象再做相应处理。
#### 跨线程异步调用破坏了原有事务边界
对于多线程环境下发起的任务来说,由于每个新启动出来的子线程都拥有独立堆栈空间以及局部变量表结构等原因使得它们之间相互隔离互不影响。因此即使父级进程中存在有效的事务范围也难以传递给这些新开辟出来的工作单元使其继承下来继续沿用下去[^3]。
```java
@Transactional
void asyncOperation(){
CompletableFuture.runAsync(() -> {/* 这里的代码不是在事务范围内*/});
}
```
要解决这个问题可以在配置文件里指定合适的任务执行器,并确保所有涉及数据库变更的操作都在主线程完成后再提交结果给其他协作者知晓即可。
#### 错误异常捕获阻止回滚发生
默认状况之下只要遇到未被捕获住的 RuntimeException 或者 Error 类型错误就会自动触发整个过程来回退至最初状态之前的样子。但是假如有开发者不小心把 try-catch 块写到了不该有的地方导致那些本应抛出去供框架层面捕捉识别的信息反而提前得到了遏制的话,那么最终很可能造成部分更新成功而另一些却失败的现象出现[^2]。
```java
@Transactional(rollbackFor=Exception.class)
public void operationWithProperRollback(Exception e){
throw new Exception("This will cause rollback",e);
}
@Transactional(noRollbackFor=IllegalArgumentException.class)
public void operationWithoutRollback(IllegalArgumentException e){
throw new IllegalArgumentException("Won't trigger a rollback");
}
```
通过合理设定 `rollbackFor` 和 `noRollbackFor` 参数可以精确控制哪些类型的异常应该引起回滚动作的发生与否。
阅读全文
相关推荐
















