Spring中的事务管理
事务的属性
事务的属性是进行事务管理必不可少的一部分,事务管理会根据这些属性执行不同的操作,因此在了解事务管理之间,需要先简单知道一下有哪些属性。
1. 传播行为(Propagation Behavior)
-PROPAGATION_REQUIRED
: 如果当前有事务,加入该事务;如果没有事务,新建一个事务(这是默认设置)。
PROPAGATION_REQUIRES_NEW
: 总是新建一个事务,如果当前有事务,则挂起当前事务。PROPAGATION_SUPPORTS
: 支持当前事务,如果没有事务,则以非事务方式执行。PROPAGATION_NOT_SUPPORTED
: 总是以非事务方式执行,如果当前有事务,则挂起当前事务。PROPAGATION_MANDATORY
: 必须在已有事务中执行,否则抛出异常。PROPAGATION_NEVER
: 必须在非事务环境中执行,如果当前有事务,则抛出异常。PROPAGATION_NESTED
: 如果当前有事务,则在嵌套事务中执行。没有就新建一个
2. 隔离级别(Isolation level)
与数据库相同
ISOLATION_DEFAULT
: 使用底层数据库的默认隔离级别(这是默认设置)。ISOLATION_READ_UNCOMMITTED
: 允许读取未提交的数据,可能会导致脏读。ISOLATION_READ_COMMITTED
: 只能读取已提交的数据,避免脏读。ISOLATION_REPEATABLE_READ
: 多次读取相同数据会返回相同结果,避免不可重复读。ISOLATION_SERIALIZABLE
: 完全串行化的读取,确保事务之间完全隔离,代价最高。
3. 超时时间(Time out)
设置事务的超时时间(单位为秒)。如果事务在指定时间内未完成,将会被回滚。
默认值为-1,表示没有超时限制
4. 只读模式(Read-Only Mode)
readOnly
: 如果设置为 true
,则提示底层数据库进行只读优化。适用于只进行查询操作的事务。4
默认为false
封装类:
Spring中使用TransactionDefinition
封装这些属性,具体如下:
编程式事务
编程式事务,用来手动管理事务的边界,直接调用Spring的事务管理api来启动,提交,或回滚事务
这种方式通常是使用transactionTemplate
或platformTransactionManager
platformTransactionManager
1. 开启事务:
要开启事务,首先需要调用 PlatformTransactionManager 的 getTransaction 方法。这个方法会返回一个 TransactionStatus 对象,表示当前事务的状态。getTransaction 需要传入一个TransactionDefinition,可以自定义,也可以传一个默认的配置DefaultTransactionDefinition
2. 提交事务:
如果事务操作成功,可以调用 PlatformTransactionManager 的 commit 方法来提交事务。提交后,所有在事务中执行的操作都会被持久化。
3. 回滚事务:
如果事务操作失败或者发生了异常,可以调用 PlatformTransactionManager 的 rollback 方法来回滚事务。回滚后,事务中的所有操作都会被撤销。
@Autowired
private PlatformTransactionManager transactionManager;
public void updateSaleByManager(int id, int stock) {
TransactionStatus status = transactionManager.getTransaction(
new DefaultTransactionDefinition());
try {
bookDao.updateSale(id, stock);
bookDao.updateStock(id, -stock);
int i = 1/0;
transactionManager.commit(status);
}catch (Exception e) {
transactionManager.rollback(status);
}
}
TransactionTemplate
对platformTransactionManager 进行了封装,实际还是调用的其方法,只不过简化了一些操作,供开发人员使用
在execute中开启事务,
通过status.setRollbackOnly()回滚操作,否则会自动提交
实例代码
@Autowired
private TransactionTemplate template;
public void updateSaleByTemplate(int id, int stock) {
template.execute(status -> {
bookDao.updateSale(id, stock);
bookDao.updateStock(id, -stock);
// status.setRollbackOnly();
return null;
});
}
这是execut的源码
@Nullable
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
//先判空
Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
//获取到 ptm
PlatformTransactionManager var3 = this.transactionManager;
/*CallbackPreferringPlatformTransactionManager
主要用于需要异步或事件驱动的事务管理场景。
这种方式允许开发者将事务逻辑封装在回调中,减少显式事务管理的复杂性。
不在本次讨论返回内,可以直接略过
*/
if (var3 instanceof CallbackPreferringPlatformTransactionManager cpptm) {
return cpptm.execute(this, action);
}
//主要逻辑!!
else {
//开启事务
TransactionStatus status = this.transactionManager.getTransaction(this);
Object result; //定义结果变量
try {
result = action.doInTransaction(status); //执行我们刚刚写的代码
}
//捕获异常,并回滚
catch (Error | RuntimeException var6) {
this.rollbackOnException(status, var6);
throw var6;
} catch (Throwable var7) {
this.rollbackOnException(status, var7);
throw new UndeclaredThrowableException(var7, "TransactionCallback threw undeclared checked exception");
}
//提交事务
this.transactionManager.commit(status);
return result;
}
}
声明式事务
声明式事务管理是 Spring 中更为常用的事务处理方式,利用 AOP(面向切面编程)来处理事务管理。
@Transactional
是最常用的方式,只需将其应用到类或方法上,Spring 就会自动为你管理事务。注解中可以定义事务的属性,此外,声明式还可以配置只对某些特性的异常进行回滚,而忽略其他异常
注解失效的情况
- 首先事务是需要数据库支持的,mysql中
nnoDB
支持事务,如果使用其他的引擎 如MyISAM
,那么不支持事务,自然就会失效 - 注解生效的前提是
Spring
通过代理,对其进行增强,那么,只要没有被代理,就会失效,以下是几种代理失效的情况
- 直接在本类中调用方法,不会走代理对象,那就不会生效,解决的话可以考虑
AspectJ
- 静态方法不会被代理
- 没有被Spring管理 ,没有被Spring管理的对象,无法被检查事务注解的拦截器拦截,那就无法对Bean进行代理增强,
- 没有配置事务管理器
- 事务传播方式为
NOT_SUPPORTED
||NEVER
- 异常被吃掉,如果在方法体里手动处理了异常而没有抛出,那么就不会被代理后对象方法检查到,自然无法回滚
- 异常类型错误:注解默认回滚的异常是
RunTimeException
,如果想触发其他异常回滚,需要手动定义rollbackFor
@Transactional在非Public方法上注解会失效?
如果你之前看过有关注解的失效场景的话,会发现几乎都提到了,注解写到非Public方法上会失效,因为JDK动态代理只能代理实现接口的方法,而接口的访问修饰符就是public
此外 ,在检查事务属性时,也会判断方法是否为public如果是非public
会返回null
检查事务属性的方法在AbstractFallbackTransactionAttributeSource
类中
@Nullable
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
//allowPublicMethodsOnly()会返回true,从而会检查方法是否为public
if (this.allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
} else {
……
}
}
但在SpringBoot 3 中,已经将这个方法的返回值改为false
,那么就不会执行后面的校验,因此,非public方法也可以被代理了
默认实现方法 在 AbstractFallbackTransactionAttributeSource
中
protected boolean allowPublicMethodsOnly() {
return false;
}
在继承类中的实现,其中publicMethodsOnly
为成员变量,在初始化的时候被赋值
protected boolean allowPublicMethodsOnly() {
return this.publicMethodsOnly; //这个publicMethodsOnly是成员变量,在初始化时就被设为false
}
通过查阅资料与询问gpt,可以发现,在SpringBoot的自动配置导入过程中,会执行这么一段代码
@Bean
@Role(2)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource(false);//在Spring2中,使用的是无参构造,而无参构造 publicMethodsOnly 默认为真
}