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

相关推荐
星星电灯猴4 分钟前
iOS 性能调试全流程:从 Demo 到产品化的小团队实战经验
后端
程序无bug13 分钟前
手写Spring框架
java·后端
JohnYan15 分钟前
模板+数据的文档生成技术方案设计和实现
javascript·后端·架构
阿蒙Amon21 分钟前
C#读写文件:多种方式详解
开发语言·数据库·c#
全干engineer26 分钟前
Spring Boot 实现主表+明细表 Excel 导出(EasyPOI 实战)
java·spring boot·后端·excel·easypoi·excel导出
Da_秀30 分钟前
软件工程中耦合度
开发语言·后端·架构·软件工程
东窗西篱梦41 分钟前
Redis集群部署指南:高可用与分布式实践
数据库·redis·分布式
蓝易云1 小时前
Qt框架中connect()方法的ConnectionType参数使用说明 点击改变文章字体大小
linux·前端·后端
a_Dragon11 小时前
Spring Boot多环境开发-Profiles
java·spring boot·后端·intellij-idea
用户8324951417321 小时前
Maven 项目打包:实现业务代码与第三方依赖分离
后端