文章目录
@Transactional 是 Spring 框架提供的用于声明式事务管理的注解。它简化了事务管理的代码,让开发者能够更方便地处理数据库事务。
1. @Transactional
定义
@Transactional
是 Spring 框架中的一个注解,主要用于声明式事务管理。它可以用于类或者方法级别,控制方法中的数据库操作是否要被事务管理。当 @Transactional
注解应用于某个方法时,该方法中的所有数据库操作(增、删、改等)会被绑定到一个事务中,保证这些操作在一个整体的事务上下文中执行。
Spring 的事务管理基于 AOP(面向切面编程),即通过在方法执行前后控制事务的开启、提交和回滚。
2. @Transactional
功能
@Transactional
注解的核心功能是声明式事务管理。它能自动处理事务的提交、回滚等复杂逻辑。主要功能如下:
- 事务的原子性:确保事务中的一组数据库操作要么全部成功,要么全部失败(即回滚)。
- 事务的一致性:保证数据在事务执行过程中,数据库始终处于一致的状态。
- 事务的隔离性:不同事务之间的数据操作互不干扰,保证在事务执行期间的数据隔离。
- 事务的持久性:事务提交后,数据的变更会被持久保存。
3. @Transactional
特点
@Transactional
注解具有以下特点和功能点:
3.1 事务的传播行为(Propagation)
事务传播行为定义了一个事务方法如何参与到现有事务中。Spring 提供了多种传播行为来满足不同的场景需求:
- REQUIRED(默认值):如果当前存在一个事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- REQUIRES_NEW:每次都会创建一个新的事务,当前存在的事务会被挂起。
- SUPPORTS:支持当前事务,如果有事务就加入;如果没有事务则以非事务方式运行。
- NOT_SUPPORTED:非事务方式运行,挂起当前事务。
- MANDATORY:必须在一个事务中执行,如果当前没有事务,则抛出异常。
- NEVER:必须在非事务环境下执行,如果当前存在事务,则抛出异常。
- NESTED:在当前事务内开启一个嵌套事务,如果外部事务回滚,嵌套事务也会回滚。
3.2 隔离级别(Isolation)
事务的隔离级别决定了一个事务在修改数据的同时,其他事务对该数据的可见性。Spring 提供了以下隔离级别:
- DEFAULT:使用数据库的默认隔离级别。
- READ_UNCOMMITTED:允许读取未提交的数据,可能导致脏读。
- READ_COMMITTED:只能读取已提交的数据,避免脏读,但可能会导致不可重复读。
- REPEATABLE_READ:确保在同一事务中多次读取相同的数据,读取的结果是相同的,避免不可重复读。
- SERIALIZABLE:最高的隔离级别,完全锁定事务访问的数据,避免脏读、不可重复读和幻读,但性能较差。
3.3 回滚规则(Rollback Rules)
- 默认情况下,Spring 只在运行时异常(
RuntimeException
或其子类)时才会回滚事务。 - 如果需要在受检异常(
Checked Exception
)发生时也回滚事务,可以通过rollbackFor
属性进行配置。
例如:
@Transactional(rollbackFor = Exception.class)
public void myMethod() {
// 发生任何异常都会回滚
}
3.4 只读事务(Read-Only Transaction)
@Transactional
可以通过 readOnly = true
来声明只读事务。这种事务适用于只执行查询操作的场景,数据库会进行一些优化。
@Transactional(readOnly = true)
public List<User> findAllUsers() {
return userRepository.findAll();
}
3.5 事务超时(Timeout)
@Transactional
可以设置事务的超时时间,事务超时后会自动回滚。timeout
参数表示允许事务执行的最大时间(单位:秒)。
@Transactional(timeout = 30) // 超时时间为30秒
public void myMethod() {
// 如果事务超过30秒未完成,将会回滚
}
3.6 并发控制(Concurrency)
通过 @Transactional
可以控制并发事务的行为,防止数据更新时产生冲突或数据丢失,避免脏读、不可重复读等问题。
3.7 异常处理
@Transactional
默认情况下只对未捕获的运行时异常进行回滚,受检异常则不会触发回滚。通过 noRollbackFor
可以指定某些异常发生时不回滚。
@Transactional(noRollbackFor = MyCustomException.class)
public void myMethod() {
// 如果发生 MyCustomException,事务不会回滚
}
4. @Transactional
使用场景
- 银行转账:在一个银行系统中,从一个账户扣款并向另一个账户转账时,必须保证两个操作要么同时成功,要么同时失败。
- 订单处理系统:在一个电商系统中,生成订单、扣减库存、扣减余额等操作必须在一个事务中,要么全部成功,要么全部回滚。
- 批量数据更新:批量更新数据时,如果中间发生异常,可以通过
@Transactional
实现全部回滚,保证数据一致性。 - 数据库备份和还原:在数据库备份和还原时,必须确保备份或还原的事务执行过程中不会影响现有数据的完整性。
5. @Transactional
具体案例
案例 1:银行转账
@Service
public class BankService {
@Autowired
private AccountRepository accountRepository;
@Transactional
public void transfer(Long fromAccountId, Long toAccountId, BigDecimal amount) {
Account fromAccount = accountRepository.findById(fromAccountId).orElseThrow();
Account toAccount = accountRepository.findById(toAccountId).orElseThrow();
// 从来源账户扣款
fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
accountRepository.save(fromAccount);
// 向目标账户加款
toAccount.setBalance(toAccount.getBalance().add(amount));
accountRepository.save(toAccount);
}
}
在这个例子中,@Transactional
保证了从一个账户扣钱、向另一个账户转钱的操作是一个原子事务。如果中途发生异常,所有的操作都会回滚。
案例 2:订单系统
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private InventoryService inventoryService;
@Autowired
private PaymentService paymentService;
@Transactional
public void placeOrder(Order order) {
// 扣减库存
inventoryService.reduceStock(order.getProductId(), order.getQuantity());
// 扣减账户余额
paymentService.deductBalance(order.getUserId(), order.getTotalPrice());
// 保存订单
orderRepository.save(order);
}
}
在此例中,@Transactional
保证下单时的扣减库存、扣减账户余额、保存订单三个操作要么一起成功,要么一起回滚,避免出现下单成功但扣减库存或扣钱失败的问题。
案例 3:只读查询
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional(readOnly = true)
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
}
在这个例子中,通过 @Transactional(readOnly = true)
声明该方法为只读事务,提升查询性能并确保不会产生不必要的写锁。
6. 总结
@Transactional
是 Spring 提供的声明式事务管理工具,用于简化事务管理,确保业务操作的原子性和数据的一致性。- 它具有多种功能,包括事务传播、隔离级别、回滚规则、超时控制等,可以灵活地应对各种事务管理需求。
- 适用场景包括银行转账、订单系统等需要保证数据一致性的业务场景。
希望对你有所帮助,若有问题欢迎指正~😊