Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/spring-boot/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Spring嵌套事务_Spring_Spring Boot_Spring Data_Spring Data Jpa_Spring Transactions - Fatal编程技术网

Spring嵌套事务

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);

在我的Spring Boot项目中,我实现了以下服务方法:

@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允许您定义

  • 应回滚哪个事务的异常
  • 不应回滚的事务的异常
尝试注释您的子方法:使用@Transactional(no rollback for=“ExceptionName”)或您的父方法进行更新。

使用“自我”注入模式可以解决此问题

示例代码如下所示:

@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();
        }
    } 
    

显然,您正在调用同一类中的一个方法,因此Spring无法拦截该调用并应用事务代理(忽略
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(() -> {
          // ...
      });
   }
}