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 {@Autowiredprivate UserRepository userRepository;@Transactionalpublic 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 {// ...
}
性能优化
事务虽好,但也不是没有成本的。在某些高并发场景下,过多的事务操作可能会成为性能瓶颈。为了优化性能,我们可以通过以下几种方式:
- 减少事务范围:尽量让事务只包含那些必须要在同一事务中完成的操作。
- 只读事务:如果事务只涉及到数据的读取,可以将事务标记为只读,这样可以帮助数据库优化事务处理。
@Transactional(readOnly = true)
public User findUserById(Long id) {return userRepository.findById(id).orElse(null);
}
失效场景
在使用Spring事务的时候,有些情况可能会导致事务失效,比如:
- 自调用问题:在同一个类中,一个非事务方法调用事务方法,事务是不会起作用的。
- 异常处理:如果你在事务方法中捕获了所有异常,并没有重新抛出,事务是不会回滚的。
使用场景
事务通常用在需要保证一系列操作要么全部成功,要么全部失败的场景,比如:
- 用户注册时,需要同时创建用户记录和用户的初始数据。
- 订单支付时,需要更新订单状态和用户的账户余额。
代码示例
让我们再看一个例子,模拟用户转账的场景:
@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事务的传播行为定义了事务方法之间的交互方式。举个例子,我们来看REQUIRED
和REQUIRES_NEW
传播行为的区别。
@Service
public class AccountService {@Autowiredprivate 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 {@Autowiredprivate TransactionTemplate transactionTemplate;public void executeComplexLogic() {transactionTemplate.execute(new TransactionCallback<Void>() {@Overridepublic 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() {@Overridepublic void afterCommit() {// 记录事务提交后的日志}@Overridepublic void afterCompletion(int status) {if (status == TransactionSynchronization.STATUS_ROLLED_BACK) {// 记录事务回滚的日志}}});
}
通过这些示例,你可以看到Spring事务管理在不同场景下的应用。
理解这些复杂场景对于能够在实际开发中灵活运用Spring事务管理至关重要。
记住,每个场景都有其特殊性,选择正确的事务策略可以帮助你避免许多常见的问题。
核心要点
- 基本使用:使用
@Transactional
注解来声明事务,这是Spring提供的一种声明式事务管理方式。 - 事务传播行为:Spring事务的传播行为定义了事务之间的相互作用,如
REQUIRED
,REQUIRES_NEW
,NESTED
等,这决定了事务是否共享或独立。 - 事务的隔离级别:隔离级别(如
READ_COMMITTED
,SERIALIZABLE
等)控制事务之间的可见性,防止诸如脏读、不可重复读、幻读等问题。 - 事务的回滚规则:默认情况下,Spring仅在运行时异常发生时回滚事务。可通过
rollbackFor
自定义回滚条件。 - 超时和只读设置:可以设置事务的超时时间和声明只读事务,以优化性能和资源利用。
高级应用场景
- 编程式事务管理:通过
TransactionTemplate
或直接使用PlatformTransactionManager
来手动管理事务。 - 嵌套事务:通过
NESTED
传播行为实现嵌套事务,内部事务失败不影响外部事务。 - 异步和事务:异步方法中使用事务需要特别注意,由于执行线程的不同,可能影响事务的管理。
- 事务同步管理:使用
TransactionSynchronizationManager
进行事务的细粒度控制,如在事务提交或回滚后执行特定操作。 - 异常处理与事务回滚:异常处理在事务中非常重要,不当的异常处理可能导致事务不回滚,引起数据不一致。
实际应用建议
- 合理设计事务范围:避免将大量操作包含在单一事务中,以减少资源锁定时间和提高性能。
- 注意异常处理:确保适当的异常抛出,以触发事务回滚。
- 避免在异步方法中使用事务:或者确保你理解如何在多线程环境下正确管理事务。
- 谨慎使用嵌套事务:它们可能会增加复杂性和性能开销。
- 监控和调优:在生产环境中监控事务的性能,根据需要调整事务策略和配置。
总之,SpringBoot中的事务管理是一个强大但需要谨慎使用的工具。
理解它的工作原理和应用场景,可以帮助你更有效地管理数据一致性和应用性能。
记住,每个应用的需求不同,所以在使用事务时,总是要考虑到你的具体场景和需求。
最后说一句(求关注,求赞,别白嫖我)
最近无意间获得一份阿里大佬写的刷题笔记,一下子打通了我的任督二脉,进大厂原来没那么难。
这是大佬写的, 7701页的BAT大佬写的刷题笔记,让我offer拿到手软
项目文档&视频:
项目文档 & 视频
本文,已收录于,我的技术网站 ddkk.com,有大厂完整面经,工作技术,架构师成长之路,等经验分享
求一键三连:点赞、分享、收藏
点赞对我真的非常重要!在线求赞,加个关注我会非常感激