Spring @Transactional(propagation = propagation.REQUIRES_NEW)doesn't work if stream is open?

ffscu2ro  于 2023-09-29  发布在  Spring
关注(0)|答案(1)|浏览(136)

很难提供这方面的示例代码。让我试着用一些伪代码来解释它。如果需要的话,我会给出一个可行的例子。
首先,我将演示代码的一个变体,它可以按预期工作。在这个变体中,我迭代了一个实体列表。对于每个实体,我调用一个服务方法,该方法创建一个新事务并向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()中完成的插入在基于Stream的版本中丢失,而在基于List的版本中没有丢失。
如果在流打开时创建新事务,它以某种方式绑定到外部事务,这是已知的行为吗?或者这可能是一个Spring的bug?

g2ieeal7

g2ieeal71#

我写了一小段代码来说明这个问题。这使我更容易测试和查明问题。我发现在独立的Sping 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">

相关问题