spring Spring嵌套事务

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37217075/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 01:05:31  来源:igfitidea点击:

Spring nested transactions

springspring-bootspring-dataspring-data-jpaspring-transactions

提问by alexanoid

In my Spring Boot project I have implemented following service method:

在我的 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;
}

inside of this method I use another service method which is called update:

在此方法中,我使用另一种服务方法,称为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);
}

I need to commit changes to database in updatemethod independently of the owner transaction which is started in validateBoardmethod. Right now any changes is rolling back in case of any exception.

我需要在update方法中独立于在validateBoard方法中启动的所有者事务提交对数据库的更改。现在,如果出现任何异常,任何更改都会回滚。

Even with @Transactional(propagation = Propagation.REQUIRES_NEW)it doesn't work.

即使有@Transactional(propagation = Propagation.REQUIRES_NEW)它也不起作用。

How to correctly do this with Spring and allow nested transactions ?

如何使用 Spring 正确执行此操作并允许嵌套事务?

回答by Jakub Bibro

This documentation covers your problem - https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#transaction-declarative-annotations

本文档涵盖了您的问题 - https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#transaction-declarative-annotations

In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional. Also, the proxy must be fully initialized to provide the expected behaviour so you should not rely on this feature in your initialization code, i.e. @PostConstruct.

在代理模式下(默认),只有通过代理进入的外部方法调用才会被拦截。这意味着自调用实际上是目标对象中的一个方法调用目标对象的另一个方法,即使被调用的方法用@Transactional 标记,也不会在运行时导致实际事务。此外,代理必须完全初始化以提供预期的行为,因此您不应在初始化代码中依赖此功能,即@PostConstruct。

However, there is an option to switch to AspectJ mode

但是,有一个选项可以切换到 AspectJ 模式

回答by shenyu1997

Using "self" jnject parttner can resolve this issue.

使用“self”jnject 伙伴可以解决这个问题。

sample code like below:

示例代码如下:

@Service @Transactional
public class YourService {
   ... your member

   @Autowired
   private YourService self;   //inject proxy as instance member variable ;

   @Transactional(propagation= Propagation.REQUIRES_NEW)
   public void methodFoo() {
      //...
   }

   public void methodBar() {
      //call self.methodFoo() rather than this.methodFoo()
      self.methodFoo();
   }
}

The point is using "self" rather than "this".

重点是使用“自我”而不是“这个”。

回答by Philip Dilip

The basic thumb rule in terms of nested Transactions is that they are completely dependent on the underlying database, i.e. support for Nested Transactions and their handling is database dependent and varies with it. In some databases, changes made by the nested transaction are not seen by the 'host' transaction until the nested transaction is committed. This can be achieved using Transaction isolation in @Transactional (isolation = "")

嵌套事务的基本经验法则是它们完全依赖于底层数据库,即支持嵌套事务,它们的处理取决于数据库并随数据库而变化。在某些数据库中,在提交嵌套事务之前,“主机”事务不会看到嵌套事务所做的更改。这可以使用@Transactional 中的事务隔离来实现(isolation = "")

You need to identify the place in your code from where an exception is thrown, i.e. from the parent method: "validateBoard" or from the child method: "update".

您需要确定代码中抛出异常的位置,即来自父方法:“validateBoard”或来自子方法:“update”。

Your code snippet shows that you are explicitly throwing the exceptions.

您的代码片段显示您明确抛出异常。

YOU MUST KNOW::

你必须知道::

In its default configuration, Spring Framework's transaction infrastructure code only marks a transaction for rollback in the case of runtime, unchecked exceptions; that is when the thrown exception is an instance or subclass of RuntimeException.

在其默认配置下,Spring Framework 的事务基础结构代码仅在运行时、未检查异常的情况下将事务标记为回滚;那是当抛出的异常是 RuntimeException 的实例或子类时。

But @Transactional never rolls back a transaction for any checked exception.

但是@Transactional 永远不会为任何已检查的异常回滚事务。

Thus, Spring allows you to define

因此,Spring 允许您定义

  • Exception for which transaction should be rollbacked
  • Exception for which transaction shouldn't be rollbacked
  • 应回滚事务的异常
  • 不应回滚事务的异常

Try annotating your child method: update with @Transactional(no-rollback-for="ExceptionName") or your parent method.

尝试注释您的子方法:使用 @Transactional(no-rollback-for="ExceptionName") 或您的父方法更新。

回答by Shailendra

Your transaction annotation in updatemethod will not be regarded by Spring transaction infrastructure if called from some method of same class. For more understanding on how Spring transaction infrastructure works please refer to this.

update如果从同一类的某些方法调用,则 Spring 事务基础结构不会考虑方法中的事务注释。有关 Spring 事务基础结构如何工作的更多了解,请参阅

回答by xyz

Your problem is a method's call from another method inside the same proxy.It's self-invocation. In your case, you can easily fix it without moving a method inside another service (why do you need to create another service just for moving some method from one service to another just for avoid self-invocation?), just to call the second method not directly from current class, but from spring container. In this case you call proxy second method with transaction not with self-invocatio.

您的问题是来自同一代理内的另一个方法的方法调用。这是自调用。在您的情况下,您可以轻松修复它而无需在另一个服务中移动方法(为什么您需要创建另一个服务只是为了将某些方法从一个服务移动到另一个服务只是为了避免自我调用?),只需调用第二个方法不是直接来自当前类,而是来自 spring 容器。在这种情况下,您使用事务而不是自我调用调用代理第二个方法。

This principle is useful for any proxy-object when you need self-invocation, not only a transactional proxy.

当您需要自调用时,此原则对任何代理对象都很有用,而不仅仅是事务代理。

@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) {
    .......
   }
}

when you do call context.getBean(SomeService.class).methodTwo(object);container returns proxy object and on this proxy you can call methodTwo(...)with transaction.

当您调用context.getBean(SomeService.class).methodTwo(object);容器返回代理对象时,您可以在此代理上调用methodTwo(...)事务。