在 Spring Boot 中使用事务

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

事务隔离级别

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

@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)
    创建一个新的事务,如果当前存在事务,则将其挂起。
@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 方法,事务行为不会被应用。

@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周围创建一个新的代理,使其能够管理事务行为:

@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 默认情况下会自动回滚事务。这确保如果发生错误,事务内的数据更改不会被持久化。

@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 属性来自定义回滚行为。

@Service
@Transactional(noRollbackFor = RuntimeException.class)
public class UserService {
    
// ...
}

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

默认的回滚行为

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

私有方法和 @Transactional

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

@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 注解通过串行化事务提供了处理并发问题的机制。默认的隔离级别通过确保事务不相互干扰来防止大多数并发问题。

@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应用程序中进行有效的事务管理非常重要。

相关推荐

  1. Spring Boot 使用事务

    2024-01-25 00:48:01       34 阅读
  2. SpringBoot事务

    2024-01-25 00:48:01       26 阅读
  3. 策略模式--SpringBoot使用

    2024-01-25 00:48:01       30 阅读
  4. SpringBoot的WebSocket使用介绍

    2024-01-25 00:48:01       38 阅读
  5. 策略模式springboot使用

    2024-01-25 00:48:01       7 阅读
  6. Spring如何手动开启事务使用编程式事务

    2024-01-25 00:48:01       9 阅读
  7. 浅析SpringBoot事务管理

    2024-01-25 00:48:01       32 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-25 00:48:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-25 00:48:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-25 00:48:01       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-25 00:48:01       20 阅读

热门阅读

  1. 【nginx】405 not allowed问题解决方法

    2024-01-25 00:48:01       42 阅读
  2. linux 之 ln 命令

    2024-01-25 00:48:01       35 阅读
  3. c语言之循环语句练习

    2024-01-25 00:48:01       37 阅读
  4. 更改ip后还被封是ip质量的原因吗?

    2024-01-25 00:48:01       38 阅读
  5. jquery笔记

    2024-01-25 00:48:01       32 阅读
  6. 【Webpack】样式处理 - 分离样式文件

    2024-01-25 00:48:01       33 阅读
  7. python

    2024-01-25 00:48:01       36 阅读
  8. 240124

    240124

    2024-01-25 00:48:01      33 阅读