事务传播机制
Spring框架中的事务传播机制是指在一个事务方法内部调用另一个事务方法时,如何处理这两个事务之间的关系。 事务传播行为定义了当一个事务方法调用另一个事务方法时,内部方法应该如何参与外部方法的事务,或者是否应该启动一个新事务。
事务传播行为
Spring通过 TransactionDefinition
接口定义了七种不同的传播行为:
- PROPAGATION_REQUIRED (0):
- 描述:如果当前存在事务,则加入该事务;否则创建一个新的事务。
- 用途:这是最常见的事务传播行为,适用于大多数情况。
- PROPAGATION_REQUIRES_NEW (1):
- 描述:总是创建一个新的事务,并且挂起当前的事务(如果存在的话)。
- 用途:这种传播行为用于确保当前方法的操作在一个独立的新事务中执行。即使当前已经存在事务,也会创建一个新的事务来执行当前操作。
- PROPAGATION_SUPPORTS (2):
- 描述:如果当前存在事务,则加入该事务;否则以非事务的方式执行。
- 用途:这种方法可以用于那些不需要事务的方法,但如果存在事务,则可以加入。
- PROPAGATION_MANDATORY (3):
- 描述:如果当前存在事务,则加入该事务;否则抛出异常。
- 用途:这种方法可以用于那些必须在事务上下文中执行的方法。
- PROPAGATION_NOT_SUPPORTED (4):
- 描述:以非事务的方式执行,并挂起当前的事务(如果存在的话)。
- 用途:这种方法可以用于那些不应该在事务上下文中执行的方法,即使存在事务也应挂起。
- PROPAGATION_NEVER (5):
- 描述:以非事务的方式执行,如果存在当前事务,则抛出异常。
- 用途:这种方法可以用于那些绝对不应该在事务上下文中执行的方法。
- PROPAGATION_NESTED (6):
- 描述:如果当前存在事务,则在嵌套事务内执行;否则其行为与
PROPAGATION_REQUIRED
相同。 - 用途:这种方法可以用于那些需要在现有事务中创建嵌套事务的方法。 0 required 当前有事务就加入该事务 没有就创建一个新的 1 requiredNew 一定会创建一个新的事务 2 support 可以在事务方法调用 也可以在非事务方法调用 3 mandatory 必须在事务方法中调用 4 not support 以非事务的方法执行 如果发生错误了不会影响调用他的方法 5 never 不能在事务方法中执行 如果调用直接报错 6 nested 嵌套事务 如果当前没有事务 则和required一样 有则创建一个嵌套事务,如果嵌套事务发生错误,则完全回滚,但是外部事务只会回滚到保存点,外部事务可以选择选择性的提交或者回滚
- 描述:如果当前存在事务,则在嵌套事务内执行;否则其行为与
PROPAGATION_REQUIRED(0)
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional(propagation = Propagation.REQUIRED)
public void createUserAndLogIn(User user) {
userRepository.save(user); // 保存用户信息
userRepository.updateLoginTime(user.getId()); // 更新用户的登录时间
}
}
必须在一个事务中
PROPAGATION_REQUIRES_NEW(1)
使用 PROPAGATION_REQUIRES_NEW 可以确保某个操作(比如发送邮件)即使失败也不会影响主事务的正常完成。 这样,如果发送邮件失败,主事务(如保存用户数据)仍然可以成功提交
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private EmailService emailService;
@Transactional
public void createUserAndSendConfirmationEmail(User user) {
userRepository.save(user);
sendConfirmationEmail(user);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendConfirmationEmail(User user) {
emailService.sendEmail(user.getEmail(), "Welcome!", "Thank you for signing up!");
}
}
PROPAGATION_SUPPORTS(2)
允许查询在非事务方法执行,当在一个事务方法中调用时,也可以加入当前事务执行 提高性能:通过减少不必要的事务开销,提高查询性能。 增加灵活性:使方法能够适应不同的调用环境
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional(propagation = Propagation.SUPPORTS)
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
}
@Service
public class OrderService {
@Autowired
private UserService userService;
@Autowired
private InventoryRepository inventoryRepository;
@Autowired
private OrderRepository orderRepository;
@Transactional
public void createOrder(Order order) {
User user = userService.getUserById(order.getUserId());
inventoryRepository.decreaseStock(order.getProductId(), order.getQuantity());
orderRepository.save(order);
}
}
PROPAGATION_MANDATORY(3)
保证事务的一致性,确保所有操作一定在同一个事务中执行 限制方法的可重用性
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private ProductService productService;
@Transactional(propagation = Propagation.MANDATORY)
public void updateOrderStatus(Order order, String newStatus) {
// 确保此方法总是在事务上下文中执行
// 如果当前不存在事务,则抛出异常
// 更新订单状态
order.setStatus(newStatus);
orderRepository.save(order);
// 更新相关产品的库存
for (Product product : order.getProducts()) {
productService.decreaseStock(product, order.getQuantity(product));
}
}
}
PROPAGATION_NOT_SUPPORTED(4)
通常用于不需要事务的方法,但是可能被事务方法调用 减少不必要的事务开销 如果这里b出现了错误 不会影响到methodA
@Service
public class ServiceClass {
@Autowired
private AnotherService anotherService;
@Transactional
public void methodA() {
// 在事务中执行的一些操作
methodB();
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void methodB() {
// 这里执行的操作
// 如果这里涉及到数据库操作,它们将不在事务中执行
}
}
PROPAGATION_NEVER
方法不能在事务方法中执行 在a方法调用b方法会直接报错 直接抛出异常
@Service
public class ServiceClass {
@Autowired
private AnotherService anotherService;
@Transactional
public void methodA() {
methodB();
}
@Transactional(propagation = Propagation.NEVER)
public void methodB() {
}
}
PROPAGATION_NESTED
当你需要在现有事务中开启一个新的事务,但又希望这个新事务能够独立于外层事务时。 当你需要在服务层中调用多个需要事务的方法,并且希望某些操作可以在这些方法之间独立回滚。 嵌套事务:如果当前存在一个事务,则在该事务内开始一个新的事务。新的事务是当前事务的一个嵌套事务。 保存点:使用保存点来实现。外部事务有一个保存点,在嵌套事务开始时设置,这样如果嵌套事务回滚,外部事务会回滚到保存点而不是完全回滚。 回滚行为:如果嵌套事务提交,那么对数据的更改将被提交,并且对外部事务仍然可见。如果嵌套事务回滚,则只回滚到保存点,外部事务可以继续并且可以选择性地提交或回滚
a给c转账 但是要先转给b b在转给c 如果b失败了 不影响 a给b的转账
@Service
public class TransferService {
@Autowired
private AccountRepository accountRepository;
@Transactional
public void transfer(String fromAccount, String toAccount, double amount) {
// 减少fromAccount余额
Account from = accountRepository.findById(fromAccount).orElseThrow();
from.setBalance(from.getBalance() - amount);
accountRepository.save(from);
// 增加toAccount余额
Account to = accountRepository.findById(toAccount).orElseThrow();
to.setBalance(to.getBalance() + amount);
accountRepository.save(to);
}
}
@Service
public class SpecialTransferService {
@Autowired
private TransferService transferService;
@Transactional(propagation = Propagation.NESTED)
public void specialTransfer(String fromAccount, String intermediateAccount, String toAccount, double amount) {
transferService.transfer(fromAccount, intermediateAccount, amount);
try {
// 这里模拟一个可能失败的操作
transferService.transfer(intermediateAccount, toAccount, amount);
} catch (Exception e) {
// 如果这里发生异常,只有中间账户到目标账户的转账会被回滚
// 但是从原账户到中间账户的转账不会被影响
}
}
}
如果想要当前事务也回滚 则需要在catch中手动回滚
@Service
public class SpecialTransferService {
@Autowired
private TransferService transferService;
@Autowired
private PlatformTransactionManager transactionManager;
@Transactional(propagation = Propagation.NESTED)
public void specialTransfer(String fromAccount, String intermediateAccount, String toAccount, double amount) {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
transferService.transfer(fromAccount, intermediateAccount, amount);
try {
// 这里模拟一个可能失败的操作
transferService.transfer(intermediateAccount, toAccount, amount);
} catch (Exception e) {
// 如果这里发生异常,我们可以选择回滚当前事务
status.setRollbackOnly();
throw new RuntimeException("Transfer failed", e);
}
// 如果所有操作都成功,提交事务
transactionManager.commit(status);
} catch (Exception e) {
// 在这里处理任何未捕获的异常
if (!status.isCompleted()) {
transactionManager.rollback(status);
}
throw e;
}
}
}