Spring事务保证了在方法内进行的所有操作要么完全成功地提交,要么完全回滚。然而,在复杂的应用程序中,可能会涉及到多个事务操作嵌套在一起,这就需要我们考虑事务的传播行为。
Spring定义了7种事务传播行为,可以通过设置 @Transactional 注解来指定事务的传播行为:
1. REQUIRED:默认值,指方法必须在一个事务内执行。假如当前存在一个事务,方法就在该事务内执行;否则,方法将开启一个新的事务运行,并在自己的事务内执行。
2. REQUIRES_NEW:方法必须运行在它自己的事务内。如果当前存在一个事务,在该方法执行期间,原先的事务会被挂起;当方法执行完毕后,原来的事务会恢复执行。
3. SUPPORTS:方法支持当前事务,也就是说,如果当前存在一个事务,就在该事务的上下文中执行方法;如果没有当前事务,则不使用事务。
4. NOT_SUPPORTED:方法不支持事务,如果有一个存在的事务,将被挂起,在方法执行期间,不会使用该事务上下文。
5. NEVER:与NOT_SUPPORTED相似,区别在于,如果当前方法存在事务上下文,抛出异常。
6. MANDATORY:方法必须在事务上下文中执行,如果不存在事务上下文,抛出异常。
7. NESTED:嵌套事务,如果存在一个事务,则在该事务内部执行;否则,新开启一个事务。嵌套事务是有隔离性的,并且可以设置回滚规则。需要注意的是,只有特定的数据访问框架才支持这种选项,如JBDC和Hibernate等;同时,NESTED还需要外层事务提供JTA事务管理。
假设我们有一个Service类包含了两个方法:methodA()和methodB(),其中methodA()需要开启一个事务,如果methodB()内部发生了异常,则整个事务需要回滚。在这种情况下,我们可以使用REQUIRED的传播行为。
示例代码如下:
@Service
public class MyService {
@Autowired
private MyRepository myRepository;
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void methodA() {
// 执行一些数据库操作
myRepository.insertRecord();
try {
// 调用methodB方法
methodB();
} catch (Exception e) {
// 如果methodB抛出了异常,则整个事务需要回滚
throw new RuntimeException(e);
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void methodB() {
// 执行一些数据库操作
myRepository.updateRecord();
// 抛出异常
throw new RuntimeException("some error occurred");
}
}
在这段代码中,我们使用REQUIRED的传播行为来确保methodA()和methodB()都在同一个事务内执行。当methodB()抛出异常时,事务将回滚,并导致methodA()也会回滚。同时,我们还使用了REQUIRES_NEW的传播行为来确保methodB()可以独立于methodA()运行,即使methodA()存在一个活动的事务。
通过这个例子,可以看到Spring事务的传播行为可以帮助我们管理事务,从而确保数据在多个方法和组件之间的正确性和完整性。