一、SpringBoot特性
1、传播行为(Propagation)
控制事务方法的执行方式,例如是否加入已存在的事务、新建一个事务等。
REQUIRED
:支持当前事务,如果不存在则新建事务。
SUPPORTS
:支持当前事务,如果不存在则以非事务方式执行。
MANDATORY
:支持当前事务,如果不存在则抛出异常。
REQUIRES_NEW
:新建事务,如果当前存在事务,则挂起当前事务。
NOT_SUPPORTED
:以非事务方式执行操作,如果当前存在事务,则挂起当前事务。
NEVER
:以非事务方式执行操作,如果当前存在事务,则抛出异常。
NESTED
:如果当前存在事务,则在嵌套事务内执行。如果不存在事务,则新建事务。
2、隔离级别(Isolation)
控制事务的隔离程度,用于处理多个事务同时操作相同数据时可能出现的问题。
DEFAULT
:使用默认的隔离级别。
READ_UNCOMMITTED
:允许读取未提交的数据更改。
READ_COMMITTED
:只能读取已提交的数据。
REPEATABLE_READ
:可重复读取数据,即在同一事务内多次读取数据结果保持一致。
SERIALIZABLE
:最高隔离级别,确保不会出现脏读、不可重复读和幻读。
3、超时(Timeout)
设置事务超时时间,如果事务在指定时间内没有完成,则自动回滚。
timeout = 30
:设置事务超时时间为 30 秒。
4、只读(ReadOnly)
设置事务是否为只读,只读事务可以优化事务处理性能。
readOnly = true
:设置事务为只读。
5、回滚规则(RollbackRules)
设置事务回滚的规则,可以根据异常类型进行回滚。
rollbackFor = {SQLException.class, RuntimeException.class}
:设置遇到 SQLException 和 RuntimeException 异常时回滚事务。
二、事务失效场景
1、未被 Spring 托管的方法调用
如果一个事务方法内部调用了另一个对象的非 public
方法,或者调用了同一个对象内部的另一个 @Transactional
方法,事务可能会失效。
解决方案:
避免在事务方法内调用非 public
方法。
将调用的方法提取到一个 Spring 托管的 Bean 中,并通过依赖注入的方式调用。
如果无法将方法提取到 Spring Bean 中,可以考虑手动获取当前 Spring 上下文,并使用上下文对象调用方法。但这种方法不推荐,因为绕过了 Spring 的事务管理。
2、异常被捕获而不重新抛出
如果在事务方法内捕获了异常并处理,但没有重新抛出异常,事务将无法正确回滚。
解决方案:
确保异常被正确地重新抛出以触发事务回滚。
当在事务方法内捕获了异常并处理后,如果不重新抛出异常,事务将无法正确回滚。这是因为 Spring 事务管理默认只会捕获抛出的异常来触发事务回滚。如果异常被捕获并在方法内部处理后,事务管理器无法检测到异常,因此无法触发事务回滚。
要解决这个问题,可以考虑以下方案:
重新抛出异常: 在捕获到异常后,通过 throw
关键字将异常重新抛出。这样可以让事务管理器捕获到异常,从而触发事务回滚。
@Transactional
public void transactionalMethod() {
try {
// 业务逻辑
} catch (Exception e) {
// 处理异常
throw e; // 重新抛出异常
}
}
手动标记回滚: 如果捕获到异常后不希望将异常继续向上层抛出,也可以通过 TransactionAspectSupport
类手动标记事务为回滚状态。
import org.springframework.transaction.interceptor.TransactionAspectSupport;
@Transactional
public void transactionalMethod() {
try {
// 业务逻辑
} catch (Exception e) {
// 处理异常
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); // 手动标记回滚
}
}
这样,在捕获并处理异常后,事务管理器就能正确地检测到异常并触发事务回滚。
3、自调用问题
如果一个事务方法自己直接或间接地调用了自己,事务可能会失效。
解决方案:
避免方法自调用,可以将自调用的部分提取到一个新的方法中,并通过方法调用来实现逻辑。
4、多线程问题
如果一个事务方法内部启动了多个线程,而这些线程没有正确地传播事务上下文,事务可能会失效。
解决方案:
在多线程环境下,确保事务上下文能够正确传播到新线程中。可以通过使用 TransactionTemplate
来确保事务上下文正确传播。
5、事务传播行为不匹配
如果事务方法之间的传播行为设置不正确,如一个方法设置为 REQUIRED
而另一个设置为 REQUIRES_NEW
,事务可能会失效。
解决方案:
确保事务方法之间的传播行为设置正确。一般来说,如果方法内部调用了其他方法,应该保持传播行为一致,以避免事务失效的问题。