@Transactional 注解的错误使用场景不必要的使用场景 1. 无需事务的方法: 问题描述:在只包含查询或只读操
不必要的使用场景
1. 无需事务的方法:
- 问题描述:在只包含查询或只读操作的方法上使用
@Transactional
是不必要的,因为这些操作不需要事务管理。 - 示例代码:
@Transactional public String testQuery() { standardBak2Service.getById(1L); return "testB"; }
- 详细说明:虽然在这些方法上添加
@Transactional
不会对系统造成太大影响,但从代码规范和性能优化的角度来看,应该避免不必要的事务开销。
2. 事务范围过大:
- 问题描述:将
@Transactional
注解应用于整个类或抽象类,导致所有方法都被事务管理,这可能导致不必要的性能开销和事务管理的复杂性。 - 示例代码:
@Transactional public abstract class BaseService { }
- 详细说明:应该根据具体方法的业务需求来决定是否使用事务,而不是一概而论地对整个类应用事务管理。
不生效的场景
1. 私有方法:
- 问题描述:在
private
方法上使用@Transactional
不会生效,因为Spring AOP无法代理私有方法。 - 示例代码:
@Transactional private String testMerge() { testAService.testA(); testBService.testB(); return "ok"; }
- 详细说明:Spring AOP通过代理方式实现
@Transactional
注解的功能,而私有方法无法被代理,因此事务管理不会生效。
2. final
和static
方法:
- 问题描述:在
final
和static
方法上使用@Transactional
不会生效,因为这些方法无法被Spring代理。 - 示例代码:
@Transactional public static void b() { } @Transactional public final void b() { }
- 详细说明:
static
方法属于类而非实例,而final
方法不能被子类重写,因此Spring无法通过代理这些方法来实现事务管理。
3. 同类内部方法调用:
- 问题描述:在同一个类内部调用其他方法时,事务不会生效,因为这种调用不经过Spring代理。
- 示例代码:
@Transactional public String testMerge() { a(); b(); return "ok"; } public void a() { standardBakService.save(testAService.buildEntity()); } public void b() { standardBak2Service.save(testBService.buildEntity2()); throw new RuntimeException("b error"); }
- 详细说明:为了确保事务生效,应该避免在同一个类的内部方法之间直接调用,而是通过Spring容器管理的代理对象来调用。
4. Bean未被Spring管理:
- 问题描述:如果Bean没有被Spring管理,
@Transactional
不会生效。 - 示例代码:
@Service public class TestBService { @Transactional public String testB() { standardBak2Service.save(entity2); return "testB"; } }
- 详细说明:确保Bean被Spring管理,可以通过加上
@Service
、@Component
等注解来实现。
5. 异步线程调用:
- 问题描述:在异步线程中调用事务方法通常不会触发事务回滚。
- 示例代码:
@Transactional public String testMerge() { testAService.testA(); new Thread(() -> { try { testBService.testB(); } catch (Exception e) { throw new RuntimeException(); } }).start(); return "ok"; }
- 详细说明:Spring的事务管理依赖于线程本地存储(ThreadLocal),在异步线程中,事务上下文不会被自动传递,因此事务管理不会生效。
不回滚的场景
1. 错误的传播属性:
- 问题描述:
@Transactional
的propagation
属性配置错误可能导致事务不回滚。 - 示例代码:
@Transactional(propagation = Propagation.REQUIRED) public String testMerge() { testAService.testA(); testBService.testB(); return "ok"; }
- 详细说明:理解不同
propagation
属性的含义和适用场景,确保事务在预期的范围内生效。
2. 异常被捕获:
- 问题描述:在方法内部捕获异常并处理,而不重新抛出,会导致事务无法回滚。
- 示例代码:
@Transactional public String testMerge() { try { testAService.testA(); testBService.testB(); } catch (Exception e) { log.error("testMerge error:{}", e); } return "ok"; }
- 详细说明:为了确保事务能够正确回滚,应该在捕获异常后重新抛出一个RuntimeException或Error类型的异常。
3. 事务无法捕获的异常:
- 问题描述:默认情况下,事务只回滚
RuntimeException
及其子类和Error
类型的异常。其他类型的异常,如SQLException
,不会触发回滚。 - 示例代码:
@Transactional(rollbackFor = Exception.class) public String testMerge() throws Exception { try { testAService.testA(); testBService.testB(); } catch (Exception e) { log.error("testMerge error:{}", e); throw new Exception(e); } return "ok"; }
- 详细说明:通过在
@Transactional
注解中指定rollbackFor
属性,可以扩大事务回滚的范围,包括非运行时异常。
转载自:https://juejin.cn/post/7413591970060337163