Spring Boot中的事务是如何实现的?懂吗?

SpringBoot中的事务管理,用得好,能确保数据的一致性和完整性;用得不好,可能会给性能带来不小的影响哦。

基本使用

在SpringBoot中,事务的使用非常简洁。首先,得感谢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(String name) {
        User user = new User(name);
        userRepository.save(user);
        // 这里假设有其他的逻辑操作
    }
}

在这个例子中,我们通过@Transactional注解标记了createUser方法。这意味着,当这个方法被调用时,Spring会为我们自动创建一个事务。如果方法正常执行完毕,事务就会提交;如果遇到异常,事务就会回滚,确保数据的一致性。

最近无意间获得一份阿里大佬写的刷题笔记,一下子打通了我的任督二脉,进大厂原来没那么难。

这是大佬写的, 7701页的BAT大佬写的刷题笔记,让我offer拿到手软

开启事务

虽然我们已经看到了如何使用@Transactional,但是你知道Spring是如何开启事务的吗?其实,当我们使用@Transactional注解时,Spring会通过AOP(面向切面编程)在运行时创建代理对象,来管理事务的开启和关闭。这个过程对我们来说是透明的,但了解其背后的机制对于深入理解Spring事务是很有帮助的。

事务回滚

默认情况下,如果被@Transactional注解的方法抛出了运行时异常(RuntimeException)或者Error,Spring就会回滚事务。但是,如果你想让事务在遇到非运行时异常时也回滚,可以这样做:

@Transactional(rollbackFor = Exception.class)
public void createUserWithRollbackForException(String name) throws Exception {
    // ...
}

性能优化

事务虽好,但也不是没有成本的。在某些高并发场景下,过多的事务操作可能会成为性能瓶颈。为了优化性能,我们可以通过以下几种方式:

  1. 减少事务范围:尽量让事务只包含那些必须要在同一事务中完成的操作。
  2. 只读事务:如果事务只涉及到数据的读取,可以将事务标记为只读,这样可以帮助数据库优化事务处理。
@Transactional(readOnly = true)
public User findUserById(Long id) {
    return userRepository.findById(id).orElse(null);
}

失效场景

在使用Spring事务的时候,有些情况可能会导致事务失效,比如:

  1. 自调用问题:在同一个类中,一个非事务方法调用事务方法,事务是不会起作用的。
  2. 异常处理:如果你在事务方法中捕获了所有异常,并没有重新抛出,事务是不会回滚的。

使用场景

事务通常用在需要保证一系列操作要么全部成功,要么全部失败的场景,比如:

  • 用户注册时,需要同时创建用户记录和用户的初始数据。
  • 订单支付时,需要更新订单状态和用户的账户余额。

代码示例

让我们再看一个例子,模拟用户转账的场景:

@Transactional
public void transfer(Long fromId, Long toId, BigDecimal amount) {
    User fromUser = userRepository.findById(fromId).orElseThrow();
    User toUser = userRepository.findById(toId).orElseThrow();
    
    fromUser.setBalance(fromUser.getBalance().subtract(amount));
    toUser.setBalance(toUser.getBalance().add(amount));
    
    userRepository.save(fromUser);
    userRepository.save(toUser);
}

在这个例子中,我们通过事务确保了转账操作的原子性。如果在转账过程中发生任何异常,比如余额不足,整个操作都会回滚,保证账户的数据一致性。

SpringBoot中事务管理的一些更高级和具体的应用场景

示例1:声明式事务的传播行为

Spring事务的传播行为定义了事务方法之间的交互方式。举个例子,我们来看REQUIREDREQUIRES_NEW传播行为的区别。

@Service
public class AccountService {

    @Autowired
    private TransferService transferService;

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        // 这里的操作在methodA的事务范围内
        transferService.methodB();
        // 如果methodB出错,整个methodA都会回滚
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() {
        // 这里的操作有自己的独立事务
        // 即使methodA失败了,methodB的操作还是会提交
    }
}

示例2:编程式事务管理

除了声明式事务,Spring还支持编程式事务管理,这在某些复杂的场景下非常有用。

@Service
public class ComplexService {

    @Autowired
    private TransactionTemplate transactionTemplate;

    public void executeComplexLogic() {
        transactionTemplate.execute(new TransactionCallback<Void>() {
            @Override
            public Void doInTransaction(TransactionStatus status) {
                // 这里是你的业务逻辑
                // 如果需要回滚,可以调用 status.setRollbackOnly();
                return null;
            }
        });
    }
}

示例3:事务的隔离级别

事务的隔离级别决定了一个事务可能受其他并发事务影响的程度。比如,我们来看看如何设置隔离级别:

@Transactional(isolation = Isolation.SERIALIZABLE)
public void transferMoney(Long fromAccountId, Long toAccountId, BigDecimal amount) {
    // 这个方法会以最高的隔离级别运行,以避免并发事务带来的问题
    // 但是性能可能会受影响
}

示例4:事务超时设置

在某些长时间运行的事务中,你可能需要设置事务的超时时间,以避免长时间占用资源。

@Transactional(timeout = 10) // 10秒超时
public void processLargeData() {
    // 这个方法如果运行超过10秒,事务会被标记为回滚
}

示例5:事务回滚的条件自定义

有时候,你可能需要自定义事务回滚的条件。比如,只在特定的异常出现时才回滚。

@Transactional(rollbackFor = {CustomException.class})
public void updateUserDetails(User user) throws CustomException {
    // 这个方法只在CustomException抛出时才回滚
    // 其他异常不会触发回滚
}

示例6:嵌套事务

嵌套事务允许在一个事务内部开始一个新的事务。如果内部事务失败,它会回滚到它开始的状态,而不影响外部事务。

@Transactional
public void parentMethod() {
    // 父事务的操作...

    try {
        nestedMethod();
    } catch (Exception e) {
        // 处理内部事务异常,父事务可以继续
    }

    // 父事务的其他操作...
}

@Transactional(propagation = Propagation.NESTED)
public void nestedMethod() {
    // 嵌套事务的操作...
}

示例7:声明式事务与异常处理

处理声明式事务时,异常的处理方式至关重要。下面是一个常见的错误处理方式。

@Transactional
public void updateUser() {
    try {
        // 更新用户数据的操作...

    } catch (Exception e) {
        // 捕获异常,这将导致事务不回滚
    }
}

在这个例子中,由于异常被捕获并没有重新抛出,事务将不会回滚,这可能会导致数据的不一致性。

示例8:使用事务同步管理器

在某些情况下,你可能需要直接与事务同步管理器进行交互,以获取当前事务的状态信息。

public void complexBusinessLogic() {
    boolean isCurrentTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();
    if (isCurrentTransactionActive) {
        // 执行依赖于当前事务的操作...
    }
}

示例9:异步方法与事务

异步方法和事务一起使用时需要特别小心,因为异步方法通常会在不同的线程中运行,这可能会导致事务管理出现问题。

@Async
@Transactional
public Future<String> asyncMethodWithTransaction() {
    // 异步操作,但事务可能不会按预期工作
    // 因为它可能在不同的线程中执行
    return new AsyncResult<>("Done");
}

示例10:事务日志记录

在某些业务场景中,你可能需要记录事务的执行情况,特别是在事务提交或回滚时。

@Transactional
public void transactionalMethodWithLogging() {
    // 事务操作...

    TransactionSynchronizationManager.registerSynchronization(
        new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                // 记录事务提交后的日志
            }

            @Override
            public void afterCompletion(int status) {
                if (status == TransactionSynchronization.STATUS_ROLLED_BACK) {
                    // 记录事务回滚的日志
                }
            }
        }
    );
}

通过这些示例,你可以看到Spring事务管理在不同场景下的应用。

理解这些复杂场景对于能够在实际开发中灵活运用Spring事务管理至关重要。

记住,每个场景都有其特殊性,选择正确的事务策略可以帮助你避免许多常见的问题。

核心要点

  1. 基本使用:使用@Transactional注解来声明事务,这是Spring提供的一种声明式事务管理方式。
  2. 事务传播行为:Spring事务的传播行为定义了事务之间的相互作用,如REQUIREDREQUIRES_NEWNESTED等,这决定了事务是否共享或独立。
  3. 事务的隔离级别:隔离级别(如READ_COMMITTEDSERIALIZABLE等)控制事务之间的可见性,防止诸如脏读、不可重复读、幻读等问题。
  4. 事务的回滚规则:默认情况下,Spring仅在运行时异常发生时回滚事务。可通过rollbackFor自定义回滚条件。
  5. 超时和只读设置:可以设置事务的超时时间和声明只读事务,以优化性能和资源利用。

高级应用场景

  1. 编程式事务管理:通过TransactionTemplate或直接使用PlatformTransactionManager来手动管理事务。
  2. 嵌套事务:通过NESTED传播行为实现嵌套事务,内部事务失败不影响外部事务。
  3. 异步和事务:异步方法中使用事务需要特别注意,由于执行线程的不同,可能影响事务的管理。
  4. 事务同步管理:使用TransactionSynchronizationManager进行事务的细粒度控制,如在事务提交或回滚后执行特定操作。
  5. 异常处理与事务回滚:异常处理在事务中非常重要,不当的异常处理可能导致事务不回滚,引起数据不一致。

实际应用建议

  • 合理设计事务范围:避免将大量操作包含在单一事务中,以减少资源锁定时间和提高性能。
  • 注意异常处理:确保适当的异常抛出,以触发事务回滚。
  • 避免在异步方法中使用事务:或者确保你理解如何在多线程环境下正确管理事务。
  • 谨慎使用嵌套事务:它们可能会增加复杂性和性能开销。
  • 监控和调优:在生产环境中监控事务的性能,根据需要调整事务策略和配置。

总之,SpringBoot中的事务管理是一个强大但需要谨慎使用的工具。

理解它的工作原理和应用场景,可以帮助你更有效地管理数据一致性和应用性能。

记住,每个应用的需求不同,所以在使用事务时,总是要考虑到你的具体场景和需求。

最后说一句(求关注,求赞,别白嫖我)

最近无意间获得一份阿里大佬写的刷题笔记,一下子打通了我的任督二脉,进大厂原来没那么难。

这是大佬写的, 7701页的BAT大佬写的刷题笔记,让我offer拿到手软

项目文档&视频:

项目文档 & 视频

本文,已收录于,我的技术网站 ddkk.com,有大厂完整面经,工作技术,架构师成长之路,等经验分享

求一键三连:点赞、分享、收藏

点赞对我真的非常重要!在线求赞,加个关注我会非常感激

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2023-12-11 11:12:02       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-11 11:12:02       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-11 11:12:02       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-11 11:12:02       18 阅读

热门阅读

  1. React学习笔记

    2023-12-11 11:12:02       36 阅读
  2. 线程组、线程切换、线程异常

    2023-12-11 11:12:02       44 阅读
  3. scheduleatfixedrate详解

    2023-12-11 11:12:02       39 阅读
  4. Presto集群安装部署

    2023-12-11 11:12:02       44 阅读
  5. springboot自定义cron定时任务执行

    2023-12-11 11:12:02       37 阅读
  6. 第三十一章 控制到 XML 模式的映射 - %ListOfDataTypes

    2023-12-11 11:12:02       23 阅读
  7. SAP ABAP 对象ALV的一些功能(ALV资料五)

    2023-12-11 11:12:02       29 阅读
  8. # C语言——预处理(#define,#if..)

    2023-12-11 11:12:02       28 阅读
  9. TS条件类型、断言及名义类型

    2023-12-11 11:12:02       36 阅读