java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Springboot @Transactional使用

Springboot @Transactional使用时需注意的几个问题记录

作者:moxiaoran5753

本文详细介绍了Spring Boot中使用`@Transactional`注解进行事务管理的多个方面,包括事务的隔离级别(如REPEATABLE_READ)和传播行为(如REQUIRES_NEW),并指出了在同一个类中调用事务方法时可能遇到的问题以及解决方案,感兴趣的朋友跟随小编一起看看吧

一、事务的隔离级别

在Springboot应用中,如果我们想实现方法一旦执行有异常产生,就触发事务回滚,可以在方法上面添加@Transactional注解。如果应用采用mysql数据库,虽然mysql本身也有事务隔离机制,但在Sping+数据库的应用中,会以Spring事务为准。mysql定义的事务隔离级别为可重复读,在使用 Spring Boot 和 MySQL 的组合时,如果你不特别指定隔离级别,那么实际使用的将是 MySQL 的默认值 REPEATABLE READ。如果在一些特定场景中不想使用可重复读,可通过@Transactional注解的isolation属性来指定。isolation支持的选项有:

使用示例:

@Transactional(isolation = Isolation.READ_COMMITTED)
public void performTransaction() {
    // 业务逻辑代码
}

 二、事务的传播行为

事务的传播行为是指当一个事务方法被另一个事务方法调用时,两者之间的事务应该如何关联。通过配置不同的传播行为,可以控制是否应该创建新的事务、加入现有事务或者以非事务方式执行等。

Spring 提供了七种标准的事务传播行为,它们可以通过 @Transactional 注解的 propagation 属性来指定。以下是这些传播行为的详细说明:

PROPAGATION_REQUIRED (默认):
    如果当前存在事务,则加入该事务;如果不存在,则创建一个新的事务。
   这是最常用的传播行为,适用于大多数场景。
PROPAGATION_SUPPORTS
    如果当前存在事务,则加入该事务;如果不存在,则以非事务方式执行。
    适合那些对事务性没有特别要求的操作,如查询操作。
PROPAGATION_MANDATORY:
    如果当前存在事务,则加入该事务;如果不存在,则抛出异常(IllegalTransactionStateException)。
    用于强制要求在已有事务中执行的方法。
PROPAGATION_REQUIRES_NEW:
    创建一个新的事务,如果当前存在事务,则将当前事务挂起。
    适用于需要独立于外部事务执行的业务逻辑,确保内部操作不会影响外部事务的结果。
PROPAGATION_NOT_SUPPORTED:
    以非事务方式执行操作,如果当前存在事务,则将当前事务挂起。
    适合那些明确不需要事务的操作,如读取系统配置或发送邮件等。
PROPAGATION_NEVER:
    以非事务方式执行,如果当前存在事务,则抛出异常(IllegalTransactionStateException)。
    用于严格禁止在事务环境中执行的方法。
PROPAGATION_NESTED:
    如果当前存在事务,则在嵌套事务内执行;如果不存在,则创建一个新的事务。
    嵌套事务是外部事务的一部分,但可以独立于外部事务进行提交或回滚。这种传播行为依赖于底层数据库和驱动的支持,例如 MySQL 的 InnoDB 引擎支持保存点(SAVEPOINT),从而实现嵌套事务。

 注意事项
性能考虑:选择合适的传播行为对于性能优化非常重要。例如,PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NOT_SUPPORTED 都会涉及到事务的挂起和恢复,这可能会带来额外的开销。
事务边界:正确理解事务的边界以及传播行为的影响,有助于避免潜在的问题,如死锁、数据不一致等。
嵌套事务支持:不是所有的数据库都支持嵌套事务。使用 PROPAGATION_NESTED 时,请确保你的数据库和驱动程序支持这一特性。
根据应用的具体需求选择适当的传播行为,可以帮助你更好地管理事务,确保数据的一致性和完整性。

三、Spring事务中存在的坑

在同一个类里面,编写两个方法,内部调用的时候,会导致事务设置失效。原因是没有用到
代理对象的缘故。具体来说:

Spring 使用 AOP 来实现事务管理,它会为每个带有 @Transactional 注解的方法创建一个代理对象。当你通过 Spring 容器获取这个类的实例并调用其方法时,实际上是调用了代理对象的方法,而不是原始类的方法。代理对象负责在方法调用前后插入事务管理逻辑。

然而,当你在一个类的非静态方法中直接调用另一个 @Transactional 方法时,调用并没有经过代理对象,而是直接调用了原始类的方法。因此,事务管理逻辑不会被应用,导致事务设置失效。

方法1:
1)、导入spring-boot-starter-aop依赖
2)、启动类添加注解@EnableAspectJAutoProxy(exposeProxy=true)
3)、事务使用的地方使用AopContext.currentProxy() 调用方法。

使用示例:

import org.springframework.aop.framework.AopContext;
@Service
public class MyService {
    @Transactional
    public void transactionalMethod() {
        // 事务逻辑
    }
    public void performOperation() {
        // 业务逻辑
        ((MyService) AopContext.currentProxy()).transactionalMethod();
    }
}

不过这种方式使得代码更加复杂且不直观,因此尽量避免使用,除非绝对必要。

最推荐的做法是将事务方法移到不同的类中。这样可以确保每次调用事务方法时都通过代理对象进行,从而保证事务管理生效。具体可参考方法2:

方法2:

@Service
public class MyService {
    @Autowired
    private AnotherService anotherService;
    public void performOperation() {
        // 业务逻辑
        anotherService.transactionalMethod();
    }
}
@Service
public class AnotherService {
    @Transactional
    public void transactionalMethod() {
        // 事务逻辑
    }
}

拓展:@Transactional支持的配置属性大盘点

除了上面提到的propagation和isolation,@Transactional 注解里边还支持配置以下属性:

1. value 或 transactionManager

@Transactional("myTransactionManager")
public void myTransactionalMethod() { // 业务逻辑 }

2. readOnly 作用:

指定事务是否为只读事务。只读事务通常用于查询操作,可以提高性能(例如,禁用脏页写入等)。

取值

@Transactional(readOnly = true)
public List<Entity> findAllEntities() { // 查询操作 }

3. timeout

作用:定义事务的超时时间(单位为秒)。如果事务在指定时间内未能完成,Spring 会自动回滚事务。
默认值:-1,表示使用后端数据库或事务管理器的默认超时设置。

@Transactional(timeout = 30)
public void longRunningOperation() { // 长时间运行的业务逻辑 }

4. rollbackFor

作用:指定哪些异常应该触发事务回滚。默认情况下,只有未检查异常(如 RuntimeException 及其子类)会触发回滚。你可以通过这个属性指定其他异常类型也应触发回滚。
取值:一个或多个异常类,可以用逗号分隔。

@Transactional(rollbackFor = {CustomCheckedException.class, AnotherException.class}) public void methodThatMayThrowExceptions() { // 业务逻辑 }

5. noRollbackFor

作用:指定哪些异常不应该触发事务回滚。默认情况下,所有未检查异常都会触发回滚,但你可以通过这个属性指定某些异常不应触发回滚。
取值:一个或多个异常类,可以用逗号分隔。

@Transactional(noRollbackFor = CustomCheckedException.class)
public void methodThatMayThrowCustomException() { // 业务逻辑 }

 6. validation

作用:指定是否在事务开始之前验证事务属性。如果设置为 true,Spring 会在事务开始前检查事务属性是否符合要求,如果不符则抛出异常。
默认值:false,即不进行验证。

@Transactional(validation = true)
public void validateTransactionalAttributes() { // 业务逻辑 }

到此这篇关于Springboot @Transactional使用时需注意的几个问题的文章就介绍到这了,更多相关Springboot @Transactional使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文