在 Spring Boot 中使用事务

当调用使用 @Transactional 注解的方法时,Spring Boot利用事务管理器来创建或加入事务。事务管理器监视事务的生命周期,根据操作结果进行提交或回滚。

事务隔离级别

Spring Boot支持各种事务隔离级别,包括READ_UNCOMMITTED(读取未提交的数据)、READ_COMMITTED(读取已提交的数据)、REPEATABLE_READ(可重复读)、SERIALIZABLE(串行化)。这些级别确定事务如何与其他事务和底层数据交互。根据应用程序的需求选择正确的隔离级别。

java 复制代码
@Service
public class UserService {
  @Autowired
  private UserRepository userRepository;
jj@Transactional
  public void updateUser(String username, String email) {
    User user = userRepository.findByUsername(username);
    user.setEmail(email);
    // ... other operations
  }
}

在上面的示例中,updateUser() 被标记为 @Transactional,允许Spring Boot管理事务的行为。

理解事务传播

事务行为可以根据方法的注解方式而有所不同。以下是一个关键区别:

@Transactional vs. @Transactional(propagation = Propagation.REQUIRES_NEW)

  • @Transactional 创建或加入一个事务。
  • @Transactional(propagation = Propagation.REQUIRES_NEW)
    创建一个新的事务,如果当前存在事务,则将其挂起。
java 复制代码
@Service
public class MyService {
@Transactional
    public void methodA() {
        // ... some code here
        methodB();
        // ... some code here
    }
@Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() {
        // ... some code here
    }
}

在这个例子中,methodA() 调用了 methodB()。由于 REQUIRES_NEW 传播设置,当 methodB() 开始一个新的事务时,methodA() 的事务被挂起。

在同一类内处理事务

当一个 @Transactional 方法调用同一类内的另一个 @Transactional 方法时,Spring 的默认行为值得注意:

默认行为

默认情况下,Spring 使用一种"基于代理"的方法。如果一个 @Transactional 方法调用同一类内的另一个 @Transactional 方法,事务行为不会被应用。

java 复制代码
@Service
public class MyService {
    @Autowired
    private MyService self;
ja   @Transactional
    public void methodA() {
        // ... some code here
        self.methodB();
        // ... some code here
    }
@Transactional
    public void methodB() {
        // ... some code here
    }
}

在这个例子中,methodA() 和 methodB() 都被标记为 @Transactional。然而,由于"基于代理"的方法,当从 methodA() 调用 methodB() 时,事务行为不会被应用。为了解决这个问题,可以考虑使用基于AspectJ的织入(weaving)或将 @Transactional 方法移到一个单独的类中。

跨不同Bean管理事务

当调用另一个Bean上的方法时,Spring会在目标Bean周围创建一个新的代理,使其能够管理事务行为:

java 复制代码
@Service
public class MyService {
@Autowired
    private OtherService otherService;
@Transactional
    public void methodA() {
        // ... some code here
        otherService.methodB();
        // ... some code here
    }
}
@Service
public class OtherService {
   @Transactional
    public void methodB() {
        // ... some code here
    }
}

在这个例子中,methodA() 在一个不同的Bean(OtherService)上调用了 methodB()。Spring会在 OtherService 周围创建一个新的代理,以根据 methodA() 的传播设置应用事务行为。

处理未检查的异常

当一个 @Transactional 方法抛出未检查的异常时,Spring 默认情况下会自动回滚事务。这确保如果发生错误,事务内的数据更改不会被持久化。

java 复制代码
@Service
@Transactional
public class UserService {
 @Autowired
  private UserRepository userRepository;
 public void updateUser(String username, String email) {
    User user = userRepository.findByUsername(username);
    if (user == null) {
      throw new RuntimeException("User not found");
    }
    user.setEmail(email);
    userRepository.save(user);
    throw new RuntimeException("Something went wrong");
  }
}

在这个例子中,updateUser() 被标记为 @Transactional 并抛出一个未检查的异常。默认情况下,事务将回滚,丢弃对用户电子邮件地址所做的更改。

自定义回滚行为

您可以使用 @Transactional 注解的 rollbackFor 或 noRollbackFor 属性来自定义回滚行为。

java 复制代码
@Service
@Transactional(noRollbackFor = RuntimeException.class)
public class UserService { 
// ...
}

在这个例子中,我们指定 RuntimeException 不应触发回滚。这在您希望在事务内保留更改,即使发生错误时也很有用。

默认的回滚行为

默认情况下,@Transactional 方法在任何未检查的异常发生时都会回滚事务。使用 rollbackFor 或 noRollbackFor 属性来自定义此行为。

私有方法和 @Transactional

@Transactional 仅在公共方法上工作。Spring在公共方法周围创建代理以管理事务行为。私有方法对代理不可见,无法被包装在事务上下文中。

java 复制代码
@Service
public class MyService {
   @Transactional
    public void methodA() {
        // ... some code here
        methodB();
        // ... some code here
    }
    private void methodB() {
        // ... some code here
    }
}

在这个例子中,methodA() 被标记为 @Transactional,但 methodB() 没有。要启用 methodB() 的事务行为,将其设置为公共方法,或将 @Transactional 注解移到同时调用 methodA() 和 methodB() 的方法上。

处理并发问题

Spring Boot 的 @Transactional 注解通过串行化事务提供了处理并发问题的机制。默认的隔离级别通过确保事务不相互干扰来防止大多数并发问题。

java 复制代码
@Service
public class UserService {
  @Autowired
  private UserRepository userRepository;
 @Transactional
  public void updateUser(String username, String email) {
    User user = userRepository.findByUsername(username);
    user.setEmail(email);
    // ... other operations
  }
}

在这个例子中,updateUser() 被标记为 @Transactional,并且Spring确保当多个线程同时尝试修改同一用户的电子邮件地址时,事务是串行化的。这可以防止数据不一致和竞态条件。

请记住,Spring 中 @Transactional 使用的默认隔离级别是 Isolation.DEFAULT,它与底层数据源的默认值一致。这通常导致"读已提交"的隔离级别,适用于大多数数据库。

精通 @Transactional 对于在Spring Boot应用程序中进行有效的事务管理非常重要。

相关推荐
程序猿麦小七22 分钟前
基于springboot的景区网页设计与实现
java·spring boot·后端·旅游·景区
蓝田~30 分钟前
SpringBoot-自定义注解,拦截器
java·spring boot·后端
theLuckyLong31 分钟前
SpringBoot后端解决跨域问题
spring boot·后端·python
A陈雷31 分钟前
springboot整合elasticsearch,并使用docker desktop运行elasticsearch镜像容器遇到的问题。
spring boot·elasticsearch·docker
.生产的驴32 分钟前
SpringCloud Gateway网关路由配置 接口统一 登录验证 权限校验 路由属性
java·spring boot·后端·spring·spring cloud·gateway·rabbitmq
小扳36 分钟前
Docker 篇-Docker 详细安装、了解和使用 Docker 核心功能(数据卷、自定义镜像 Dockerfile、网络)
运维·spring boot·后端·mysql·spring cloud·docker·容器
v'sir1 小时前
POI word转pdf乱码问题处理
java·spring boot·后端·pdf·word
李少兄1 小时前
解决Spring Boot整合Redis时的连接问题
spring boot·redis·后端
日里安1 小时前
8. 基于 Redis 实现限流
数据库·redis·缓存
冰逸.itbignyi1 小时前
SpringBoot之AOP 的使用
java·spring boot