Spring嵌套事务
在我的Spring Boot项目中,我实现了以下服务方法:Spring嵌套事务,spring,spring-boot,spring-data,spring-data-jpa,spring-transactions,Spring,Spring Boot,Spring Data,Spring Data Jpa,Spring Transactions,在我的Spring Boot项目中,我实现了以下服务方法: @Transactional public boolean validateBoard(Board board) { boolean result = false; if (inProgress(board)) { if (!canPlayWithCurrentBoard(board)) { update(board, new Date(), Board.AFK);
@Transactional
public boolean validateBoard(Board board) {
boolean result = false;
if (inProgress(board)) {
if (!canPlayWithCurrentBoard(board)) {
update(board, new Date(), Board.AFK);
throw new InvalidStateException(ErrorMessage.BOARD_TIMEOUT_REACHED);
}
if (!canSelectCards(board)) {
update(board, new Date(), Board.COMPLETED);
throw new InvalidStateException(ErrorMessage.ALL_BOARD_CARDS_ALREADY_SELECTED);
}
result = true;
}
return result;
}
在这个方法中,我使用了另一种叫做update
的服务方法:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Board update(Board board, Date finishedDate, Integer status) {
board.setStatus(status);
board.setFinishedDate(finishedDate);
return boardRepository.save(board);
}
我需要在update
方法中提交对数据库的更改,独立于在validateBoard
方法中启动的所有者事务。现在,如果出现任何异常,任何更改都将回滚
即使使用@Transactional(propagation=propagation.REQUIRES_NEW)
它也不起作用
如何正确地使用Spring并允许嵌套事务?如果从同一类的某个方法调用,Spring事务基础结构将不会考虑更新方法中的事务注释。有关Spring事务基础架构工作原理的更多了解,请参阅。本文档介绍了您的问题- 在代理模式(这是默认模式)下,只截获通过代理传入的外部方法调用。这意味着自调用,实际上是目标对象内调用目标对象另一个方法的方法,即使调用的方法标记为@Transactional,在运行时也不会导致实际事务。此外,代理必须完全初始化以提供预期的行为,因此您不应在初始化代码中依赖此功能,即@PostConstruct
但是,有一个切换到AspectJ模式的选项您的问题是来自同一代理中另一个方法的方法调用。这是自调用。 在您的情况下,您可以很容易地修复它,而无需在另一个服务中移动方法(为什么您需要创建另一个服务,只是为了将某个方法从一个服务移动到另一个服务,只是为了避免自调用?),只是为了不直接从当前类调用第二个方法,而是从spring容器调用第二个方法。在这种情况下,使用事务调用代理第二个方法,而不是使用自发票 当您需要自调用时,此原则对于任何代理对象都很有用,而不仅仅是事务代理
@Service
class SomeService ..... {
-->> @Autorired
-->> private ApplicationContext context;
-->> //or with implementing ApplicationContextAware
@Transactional(any propagation , it's not important in this case)
public boolean methodOne(SomeObject object) {
.......
-->> here you get a proxy from context and call a method from this proxy
-->>context.getBean(SomeService.class).
methodTwo(object);
......
}
@Transactional(any propagation , it's not important in this case)public boolean
methodTwo(SomeObject object) {
.......
}
}
当您调用
context.getBean(SomeService.class).methodTwo(object)时
container返回代理对象,在此代理上,您可以使用transaction调用methodTwo(…)
。嵌套事务的基本经验法则是它们完全依赖于基础数据库,即对嵌套事务的支持,它们的处理依赖于数据库,并随数据库而变化。
在某些数据库中,“主机”事务在提交嵌套事务之前不会看到嵌套事务所做的更改。这可以通过在@Transactional(isolation=”“)中使用事务隔离来实现
您需要标识代码中引发异常的位置,即父方法:“validateBoard”或子方法:“update”
您的代码片段显示您正在显式抛出异常
你必须知道::
在默认配置中,Spring框架的事务
在这种情况下,基础结构代码仅标记要回滚的事务
对于运行时,未检查的异常;这就是抛出异常的时间
RuntimeException的实例或子类
但是@Transactional从不为任何选中的异常回滚事务
因此,Spring允许您定义
- 应回滚哪个事务的异常
- 不应回滚的事务的异常
@Service @Transactional
public class YourService {
//... your member
@Autowired
private YourService self; //inject proxy as an instance member variable ;
@Transactional(propagation= Propagation.REQUIRES_NEW)
public void methodFoo() {
//...
}
public void methodBar() {
//call self.methodFoo() rather than this.methodFoo()
self.methodFoo();
}
}
重点是使用“自我”而不是“这个”。- 您可以创建一个新服务(CustomTransactionalService),该服务将在新事务中运行您的代码:
@Service public class CustomTransactionalService { public void test() { } @Transactional(propagation= Propagation.REQUIRES_NEW) public <U> U runInNewTransaction(final Supplier<U> supplier) { return supplier.get(); } @Transactional(propagation= Propagation.REQUIRES_NEW) public void runInNewTransaction(final Runnable runnable) { runnable.run(); } }
REQUIRES\u NEW
传播)。您应该将update
方法迁移到另一个Springbean,谢谢,现在一切正常了Spring不会以任何方式抱怨“循环依赖”吗?自我引用在我看来就像循环依赖,就像A->B->A是循环依赖一样。这段代码不会解决问题,因为如果methodFoo不工作,像您这样的嵌套事务将不会回滚,在这种情况下,您将在系统中创建功能问题
@Service
public class YourService {
@Autowired
private CustomTransactionalService customTransactionalService;
@Transactional
public boolean validateBoard(Board board) {
// ...
}
public Board update(Board board, Date finishedDate, Integer status) {
this.customTransactionalService.runInNewTransaction(() -> {
// ...
});
}
}