Spring 事务失效的常见场景

在开发 Java 企业应用程序时,事务管理是一个关键的部分。Spring 提供了强大的事务管理功能,但如果使用不当,事务可能会失效,导致数据不一致。本文将介绍 Spring 事务失效的常见场景,帮助开发者避免这些坑。

事务管理的基本概念

在开始讨论事务失效之前,先了解一下事务的基本概念。事务是一个操作序列,要么全部执行成功,要么全部执行失败。事务有四个重要特性,称为 ACID 特性:

  • Atomicity(原子性):事务中的所有操作要么全部完成,要么全部不完成。
  • Consistency(一致性):事务完成后,数据要处于一致的状态。
  • Isolation(隔离性):一个事务的执行不能被其他事务干扰。
  • Durability(持久性):事务完成后,数据应该永久保存。

在 Spring 中,事务管理通常通过 @Transactional 注解来实现。让我们来看一个简单的示例:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void createUser(User user) {
        userRepository.save(user);
    }
}

常见的事务失效场景

尽管 @Transactional 非常强大,但在某些情况下,事务管理会失效。以下是一些常见的事务失效场景及解决方法。

场景一:自调用(Self-Invocation)

自调用指的是一个类的方法在调用同一个类的另一个方法。如果这两个方法中只有一个有 @Transactional 注解,事务管理会失效。

示例

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public void createUser(User user) {
        userRepository.save(user);
        updateUser(user); // 事务在这里不会生效
    }

    @Transactional
    public void updateUser(User user) {
        user.setName("Updated Name");
        userRepository.save(user);
    }
}

解决方法

解决方法是将这些方法拆分到不同的类中,或者通过 Spring 的代理来调用这些方法。

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private UserService self;

    public void createUser(User user) {
        userRepository.save(user);
        self.updateUser(user); // 使用代理调用
    }

    @Transactional
    public void updateUser(User user) {
        user.setName("Updated Name");
        userRepository.save(user);
    }
}

场景二:非公有方法

@Transactional 注解仅在公有方法上有效。如果将其应用于私有或受保护的方法,事务管理将失效。

示例

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    private void createUser(User user) // 非公有方法
        userRepository.save(user);
    }
}

解决方法

确保 @Transactional 注解应用于公有方法。

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void createUser(User user) // 公有方法
        userRepository.save(user);
    }
}

场景三:无接口类(CGLIB 代理)

Spring 默认使用 JDK 动态代理,如果一个类没有实现接口,则事务管理会失效。

示例

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void createUser(User user) {
        userRepository.save(user);
    }
}

解决方法

通过设置 Spring 的代理策略,使用 CGLIB 代理。

application.yml 中添加以下配置:

spring:
  aop:
    proxy-target-class: true

场景四:嵌套事务

Spring 默认不支持嵌套事务。嵌套事务是在一个事务内启动另一个事务。如果内层事务失败,外层事务不会回滚。

示例

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void createUser(User user) {
        userRepository.save(user);
        try {
            updateUser(user); // 内层事务
        } catch (Exception e) {
            // 异常处理
        }
    }

    @Transactional
    public void updateUser(User user) {
        user.setName("Updated Name");
        userRepository.save(user);
        throw new RuntimeException("Update failed");
    }
}

解决方法

使用 Propagation 属性来配置事务的传播行为。

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void createUser(User user) {
        userRepository.save(user);
        try {
            updateUser(user); // 内层事务
        } catch (Exception e) {
            // 异常处理
        }
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateUser(User user) {
        user.setName("Updated Name");
        userRepository.save(user);
        throw new RuntimeException("Update failed");
    }
}

场景五:异常处理

事务默认只在未检查异常(如 RuntimeException)发生时回滚。如果捕获了异常,事务也不会回滚。

示例

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void createUser(User user) {
        userRepository.save(user);
        try {
            updateUser(user);
        } catch (Exception e) {
            // 异常处理
        }
    }

    public void updateUser(User user) {
        user.setName("Updated Name");
        userRepository.save(user);
        throw new RuntimeException("Update failed");
    }
}

解决方法

@Transactional 注解中指定回滚的异常类型。

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional(rollbackFor = Exception.class)
    public void createUser(User user
{
        userRepository.save(user);
        try {
            updateUser(user);
        } catch (Exception e) {
            // 异常处理
        }
    }

    public void updateUser(User user) {
        user.setName("Updated Name");
        userRepository.save(user);
        throw new RuntimeException("Update failed");
    }
}

结语

本文介绍了 Spring 事务失效的几种常见场景以及对应的解决方法。通过正确使用 @Transactional 注解,可以确保事务管理在应用程序中正常工作,避免数据不一致的问题。

个人观点,仅供参考,希望这篇文章对你有所帮助,祝你编码愉快!


原文始发于微信公众号(源梦倩影):Spring 事务失效的常见场景

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/291700.html

(0)
Java朝阳的头像Java朝阳

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!