在某些情况下,你会发现虽然使用了 @Transactional 注解 但是事务并没有回滚,后面的逻辑依旧继续执行了,为什么呢?
声明式事务注解为什么失效了
声明式事务失效的原因,主要由以下几点构成:
- 方法可见性 :
@Transactional仅在public方法上生效 - 自调用 :当类中的方法是通过调用同一个类当中的另外一个
@Transactional方法的时候,事务可能不会生效。这是因为这个事务注解是通过 AOP 实现的,而 Spring 的 AOP 代理机制在这种情况下不会触发。 - 异常处理 :只有
RuntimeException和Error类型的异常会触发事务回滚。如果抛出的是checked exception,事务不会回滚,除非指定rollback属性 - 代理对象 :确保是在 Spring 管理的代理对象上调用方法,如果直接使用
new关键字实例化的对象(未被 Spring 管理),那么 Spring 的 AOP 代理机制不会触发
什么是编程式事务?有哪些优点?
编程事务(Programmatic Transaction)是一种通过代码显式地管理事务的方式,而不是依赖声明式事务(Declarative Transaction)中使用的注解或者 xml 配置,在编程式事务中,开发人员通过编写代码来开启、提交和回滚事务,以精细控制事务的边界和行为
编程式事务的优点:
- 精细控制:编程事务允许开发者通过代码精细地控制事务的生命周期,包括开始、提交和回滚。可以更加具体业务需求,灵活地管理事务
- 动态处理:在允许时可以根据业务逻辑的不同情况,动态决定事务的行为。特别适合需要在代码执行过程中,根据某些条件来开启、提交或者回滚事务的场景。
- 适用于复杂事务:在一个方法中需要多次开启和关闭事务,或需要嵌套事务的复杂场景中,编程事务可以提供更大的灵活性和控制力
- 灵活性高:能够在代码中实现复炸的事务逻辑,可以精确控制事务的边界和行为,这在需要多个步骤或调用之间共享事务上下文的时候非常有用
- 性能提升:通过精细化控制事务的边界,减少不必要的事务开启和提交,从而减少事务开销;通过明确事务的开始和结束,可以确保事务范围尽可能的缩小,减少长时间占用数据库资源,提高系统的并发性,通过灵活的事务管理,可以在必要时才进行事务回滚,减少回滚操作带来的性能开销。
编程式事务的缺点:
- 代码入侵性强(核心劣势) :事务逻辑与业务 紧密耦合 在一起,业务代码中会夹杂大量的事务控制代码(比如 try-catch 中执行回滚等),这样会导致代码臃肿,可读性变差,可维护性下降
- 开发效率低,容易出错:开发者需要手动的编写事务的开启、提交和回滚,还要处理各种异常情况,增加了开发工作量,写得越多,越容易出错
- 代码冗余:如果多个业务都需要进行事务控制,会出现大量重复代码,违反了 DRY (Don't Repeat Yourself)原则,后期修改需要改动多出的代码
- 对开发人员要求高:开发人员需要熟悉事务的底层原理和相关 api 的使用,必须清除什么时候该提交,什么时候该回滚。
使用示例
下面是在 SpringBoot 使用编程式事务的两种方式,示例化代码如下
第一种
java
@Service
public class MyService {
@Resource
private PlatformTransactionManager transactionManager;
@Resource
private MyMapper myMapper;
public void myTransactionalMethod() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
// 业务逻辑代码
myMapper.insertSomething(...);
transactionManager.commit(status); // 提交事务
} catch (Exception ex) {
transactionManager.rollback(status); // 回滚事务
throw ex; // 重新抛出异常
}
}
}
transactionManager.getTransaction(...): 会创建一个新的事务,并返回 TransactionStatus 对象,这个对象会是当前事务的状态,用它来进行事务的提交和回滚
上述代码的核心流程:开启事务→ 执行业务逻辑 → 无异常提交 / 有异常回滚 。
第二种
java
@Service
public class MyService {
@Resource
private TransactionTemplate transactionTemplate;
@Resource
private MyMapper myMapper;
public void myTransactionalMethod() {
transactionTemplate.execute(status -> {
try {
// 业务逻辑代码
myMapper.insertSomething(...);
} catch (Exception ex) {
status.setRollbackOnly(); // 标记事务为回滚
throw ex; // 重新抛出异常
}
return null;
});
}
}
TransactionTemplate 是一个简化了事务的工具类,代码编写者可以将代码重心放在业务代码上,可以更加方便的实现进行提交或者回滚,使得事务处理相关的代码更加简洁、冗余更少
总结
编程式事务是通过代码显式管理事务的方式,开发者需手动编写代码控制事务的开启、提交和回滚,而非依赖声明式事务中的注解或 XML 配置,以此实现对事务边界和行为的精细控制。
推荐使用 TransactionTemplate 进行编程式事务管理
本篇文章基于 小哈书 5.13 编写,欢迎大家访问小哈书
欢迎大家访问博主的个人博客网站 AZERL7的博客-a little world for you