代码之家  ›  专栏  ›  技术社区  ›  Todd Johnson

若流是打开的,Spring@Transactional(propagation=propagation.REQUIREDS_NEW)不工作?

  •  0
  • Todd Johnson  · 技术社区  · 2 年前

    很难提供这方面的示例代码。让我试着用一些伪代码来解释一下。如果需要的话,我必须拿出一个有效的例子。

    首先,我将演示代码的变体,它可以按预期工作。在这个变体中,我正在对实体列表进行迭代。对于每个实体,我调用一个服务方法,该方法创建一个新事务并向DB写入一些内容。如果发生异常,则在该服务方法中完成的所有工作都会按预期保留在数据库中,因为这些工作是在独立于外部事务的新事务中完成的。

    下面是类/方法的伪代码,它在实体列表上迭代,并为每个实体调用一个服务方法。

    @Transactional
    public myMethod()
    {
         List<MyEntity> myEntities = myRepo.findAllBySomething("foo");
    
         foreach(MyEntity myEntity : myEntities)
         {
              myService.doSomeWork()
         }
    }
    

    以下是创建一个新事务以完成某些工作的服务方法。

    @Service
    public MyService {
    
         @Transactional(propagation = Propagation.REQUIRES_NEW)
         public void doSomeWork() {
           SomeOtherEntity someOtherEntity = new SomeOtherEntity();
           ...
           someOtherRepo.save(someOtherEntity);
         }
    }
    

    上面的示例如预期的那样工作。如果遇到异常,在MyService.doSomeWork()调用中完成的所有工作都会保留在数据库中,因为它是在独立事务中完成的。

    这就是问题的根源。我正在迭代的实体数量非常多,这导致了内存问题。因此,我没有在列表中检索它们,而是将代码更改为使用Stream。repo被更改为返回Stream而不是List,代码也被更改为如下所示。。。

    @Transactional
    public myMethod()
    {
         try (Stream<MyEntity> myStream = myRepo.findAllBySomething("foo")) {
    
              // Get an iterator to allow us to step through each row.
              Iterator<MyEntity> myStreamIterator = myStream.iterator();
    
              // Loop through each row
              while (myStreamIterator.hasNext()) {
                   // Get the next row
                   MyEntity myEntity = myStreamIterator.next();
    
                   myService.doSomeWork();
    
              }
         }
    }
    
    

    现在,如果抛出异常,则所有MyService.doSomeWork()调用中完成的工作都不在数据库中!这很奇怪。我已经打开了org.springframework.transaction的跟踪日志记录。在这两个代码实例中,我都可以看到每次调用MyService.doSomeWork()时都会创建一个新的事务。但当抛出异常时,由于某种原因,在基于流的版本中,在MyService.doSomeWork()中完成的插入会丢失,但在基于列表的版本中不会丢失。

    如果您在流打开时创建了一个新事务,那么它在某种程度上与外部事务绑定,这是已知的行为吗?或者这可能是一个Spring bug?

    0 回复  |  直到 2 年前
        1
  •  0
  •   Todd Johnson    2 年前

    我写了一小段代码来说明这个问题。这让我更容易测试和确定问题所在。我发现,当在一个独立的Spring Boot应用程序中运行这段代码时,一切都如预期的那样工作。当我将代码部署到JBoss服务器时,问题就出现了。

    我想出了解决这个问题的办法。我怀疑这更多的是一个变通办法,而不是一个解决办法。但问题似乎出在JBoss的“缓存连接管理器”上。默认情况下,它在数据源上启用。关闭“缓存连接管理器”可修复此问题。我发现了一些旧的问题,Spring人员将一些事务性问题归咎于缓存连接管理器。我可能会向Redhat和Spring提交这一问题的门票,看看是否有人愿意站出来承担责任。我怀疑他们中的任何一个都不会,但至少希望我能为下一个陷入困境的可怜人提供一些可见性。

    在有人找到问题的根源之前,要关闭缓存连接管理器,请更新数据源并将use-ccm设置为“false”,如下所示。。。

    <datasource jta="true" jndi-name="java:/myDs" pool-name="myDs" use-ccm="false">
    

    或者类似地用于XA数据源。。。

    <xa-datasource jndi-name="java:/myXaDs" pool-name="myXaDs" use-ccm="false">