SpringBoot @Transactional 事务失效场景

自调用问题(Self-Invocation)

如果一个事务性方法在同一个类中被另一个非事务性方法调用,@Transactional注解将不会生效。这是因为Spring的事务管理是通过代理实现的,自调用不会通过代理,因此不会触发事务处理。

示例

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void transactionalMethod() {
        // some transactional code
    }

    public void nonTransactionalMethod() {
        // self-invocation
        transactionalMethod(); // @Transactional will not work here
    }
}

解决方法

将调用方法和被调用方法放在不同的类中,或者使用AOP的方式来解决自调用问题。

非公共方法(Non-Public Methods)

@Transactional注解只会应用于公共方法。如果你在私有、受保护或包可见的方法上使用@Transactional注解,它将不会生效。

示例

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    private void transactionalMethod() {
        // This will not work as it's a private method
    }
}

解决方法

确保事务性方法是公共的。

没有使用Spring管理的代理

Spring的事务管理是通过AOP代理实现的。如果事务性对象没有通过Spring管理(例如,通过new关键字手动创建),那么@Transactional注解将不会生效。

示例

public class Main {
    public static void main(String[] args) {
        // Direct instantiation, no Spring proxy
        UserService userService = new UserService();
        userService.transactionalMethod(); // @Transactional will not work
    }
}

解决方法

确保所有事务性对象都是由Spring容器管理的。

事务管理器未正确配置

如果没有正确配置事务管理器,或者配置的事务管理器与实际的数据源不匹配,@Transactional注解也可能会失效。

示例

@Configuration
@EnableTransactionManagement
public class AppConfig {

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

解决方法

确保事务管理器配置正确,并与应用程序使用的数据源匹配。

事务传播行为设置不当

有时候,事务传播行为的设置会导致事务不按预期执行。例如,如果传播行为设置为Propagation.NOT_SUPPORTED,事务将会被挂起,导致@Transactional注解失效。

示例

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void nonTransactionalMethod() {
    // This method will not run in a transactional context
}

解决方法

根据业务需求正确设置传播行为。

异常类型不匹配

默认情况下,Spring只在遇到未检查异常(继承自RuntimeException)时回滚事务。如果方法抛出的是检查异常(继承自Exception),事务不会自动回滚,除非在@Transactional注解中明确指定。

示例

@Transactional
public void someMethod() throws IOException {
    throw new IOException("Checked exception"); // Transaction will not be rolled back
}

解决方法

@Transactional注解中指定rollbackForrollbackForClassName属性:

@Transactional(rollbackFor = Exception.class)
public void someMethod() throws IOException {
    throw new IOException("Checked exception"); // Transaction will be rolled back
}

多线程环境

如果在多线程环境中使用@Transactional注解,Spring事务可能会失效,因为每个线程都有自己的事务上下文。

细节参考:多线程场景下谨慎使用@Transactional注解,你不信我也没办法_@transactional 多线程-CSDN博客

示例

@Service
public class UserService {

    @Transactional
    public void transactionalMethod() {
        new Thread(() -> {
            // This code will not run in a transactional context
            // since it's executed in a different thread
        }).start();
    }
}

解决方法

1. 避免在事务性方法中使用多线程,

2. 手动管理好各个线程的事务边界

ps: 声明式事务其实有个弊端,它提交事务的时机是在方法执行完成后的,所以,锁是方法执行完释放,事务也是方法执行完才提交,那问题就出在锁刚刚释放,第二个线程立马拿到锁入栈.

改成加锁并且手动控制事务流程

结论

预防办法:确保方法是公共的,通过Spring容器管理对象,正确配置事务管理器和传播行为,并处理好异常类型和多线程环境。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值