深入理解 @Transactional 注解在 Spring 中的应用

本文详细介绍了Spring框架中的@Transactional注解在事务管理中的应用,包括其基本功能、失效情况(如自调用、异常处理等)、以及如何避免这些问题。旨在帮助开发者更好地掌握事务管理并确保数据一致性。
摘要由CSDN通过智能技术生成

        前言:在 Java 开发中,事务管理是非常重要的一环。Spring 框架提供了@Transactional注解来简化事务管理的操作,本文将深入介绍@Transactional注解的用法,并结合代码示例进行详细讨论。

1.@Transactional 注解简介

    @Transactional注解是 Spring 框架中用于管理事务的关键注解之一。通过在方法或类上添加该注解,Spring 会自动为被注解的方法创建一个事务,并在方法执行完毕后根据执行情况提交或回滚事务。这样可以确保数据库操作的原子性,保证数据的完整性。

@Service
@Transactional
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    public void updateUser(User user) {
        userRepository.save(user);
    }
}

在上面的示例中,@Transactional注解被添加到UserService类上,表示其中的方法将在事务管理下执行。

2.@Transactional 失效的情况

        尽管@Transactional注解可以很好地管理事务,但在某些情况下它可能会失效,导致事务无法正常工作。以下是一些可能导致@Transactional注解失效的情况:

2.1 自调用问题 

        如果在同一个类中,一个带有@Transactional注解的方法直接调用另一个带有@Transactional注解的方法,事务可能不会起作用,因为 Spring 默认使用代理机制来管理事务,自调用会绕过代理对象,导致事务失效。

@Service
@Transactional
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    public void updateUser(User user) {
        saveUser(user); // 这里的调用会绕过代理对象,事务失效
    }
    
    @Transactional
    public void saveUser(User user) {
        userRepository.save(user);
    }
}

2.2 异常捕获问题

        当方法内部捕获了异常并不再抛出时,事务可能会失效。Spring 默认只会对未捕获的异常进行事务回滚,如果异常被捕获并在方法内部处理,事务可能无法正常回滚。

@Service
@Transactional
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    public void updateUser(User user) {
        try {
            userRepository.save(user);
        } catch (Exception e) {
            // 异常被捕获,事务可能无法回滚
        }
    }
}

2.3 抛出非运行时异常

        异常虽然抛出了,但是抛出的是非RuntimeException类型的异常,依旧不会生效。

@Transactional
public void deleteUser() throws MyException{
    userMapper.deleteUserA();
    try {
        int i = 1 / 0;
        userMapper.deleteUserB();
    } catch (Exception e) {
        throw new MyException();
    }
}

        如果指定了回滚异常类型为Exception,那么就可以回滚非RuntimeException类型异常了。

@Transactional(rollbackFor = Exception.class)

2.4 新开启一个线程

        如下的方式deleteUserA()也不会回滚,因为spring实现事务的原理是通过ThreadLocal把数据库连接绑定到当前线程中,新开启一个线程获取到的连接就不是同一个了:

@Transactional
public void deleteUser() throws MyException{
    userMapper.deleteUserA();
	try {
		//休眠1秒,保证deleteUserA先执行
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    new Thread(() -> {
        int i = 1/0;
        userMapper.deleteUserB();
    }).start();    
}

2.5 非public方法

        如果@Transactional注解标记的方法是非public的,那么事务将失效。这是因为Spring默认使用基于代理的AOP来实现事务,而基于代理的AOP只能拦截public方法。

@Transactional
private void doSomething() {
    // 执行业务逻辑
}

2.6 未被Spring容器管理

        如果@Transactional注解标记的方法所在的类没有被Spring容器管理,那么事务将失效。这是因为Spring只会对由Spring容器管理的Bean进行事务管理。


public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Transactional
    public void updateUser(User user) {
        userRepository.save(user);
    }
}

2.7 数据库本身不支持

        数据库本身不支持事务管理。mysql数据库,必须设置数据库引擎为InnoDB。

2.8 事务传播属性设置错误

        注意传播属性的设置,比如设置了:PROPAGATION_NOT_SUPPORIED(以非事务的方式执行,如果当前有事务则把当前事务挂起)。

3. 解决方案

        针对上述问题,我们可以采取一些解决方案来确保@Transactional注解的有效性。比如避免在同一个类中使用自调用的方式,或者在捕获异常后手动抛出以触发事务回滚。

4. 总结

        通过@Transactional注解,我们可以轻松管理事务,确保数据库操作的一致性。然而,在编写代码时需要注意可能导致注解失效的情况,避免出现意外的事务行为。持续学习和实践是掌握事务管理的关键,希望本文对您有所帮助。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值