当前位置: 首页 > 知识库问答 >
问题:

Spring boot在事务结束时不需要的SQL更新

曾山
2023-03-14

我正在使用spring boot 1.5.4和hibernate core 5.2.10。

我有一个控制器,它调用一个服务方法(让我们将其命名为pService)在一些逻辑之后保存一个实体

pservice.save是这样的:

@Transactional(readOnly = false, rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public <S extends SomeEntity> S save(S entity) throws ServiceException {
    //some logic

    entity.setAttrX("original text");
    S justSavedEntity = someEntityRepository.save(entity); // POINT 1

   //we termporarily change one attribute just to get a legible representation calling another service method (lets name it eService)
   eService.process(justSavedEntity);

   return justSavedEntity; //POINT 2

}

电子服务。过程如下:

@Transactional(readOnly = false, propagation = Propagation.MANDATORY)
public void process(SomeEntity entity) {

    //some logic
    entity.setAttrX("someText");
    //method ends here
}

因为除了POINT 1之外没有其他调用要保存,所以我希望文本“原始文本”保存在数据库中,但我保存的文本是在POINT 2更改的文本。

那么,为什么我被保存为“某个文本”而不是“原始文本”?

我可以在数据库日志中看到,在第1点之后,会运行一个SQL INSERT命令,当pService返回时,会运行不需要的SQL UPDATE命令,将“原始文本”更改为“someText”,从而导致错误。

为什么发出这个“额外”UPDATE命令?

提前感谢!

共有1个答案

武嘉祥
2023-03-14

这是JPA规范所描述的预期行为。您更改了一个托管实体,从逻辑上讲,该实体与更改存储的数据是一样的,因此在事务完成之前,该实体的状态将与数据库同步。其思想是,您不必在方法末尾调用save,这是多余的。

阿诺德·加洛维奇(ArnoldGalovics)写了一篇很好的博客文章,其中有一些漂亮的图表。

考虑到这一点,很明显你不应该以这种方式改变实体。这表明你调用的“进程”没有将它传递的对象视为实体。认为当前对象似乎在不同的上下文中提供了你需要的一切,而不是检查对象是否代表相同的概念是一个好主意,这是一个常见的错误。在这种情况下,您的进程不需要实体。

您应该创建某种类型的值对象来传递给process方法。创建值对象可以很容易地将实体从其持久性上下文中分离出来:Spring JpaRepository-分离并附加实体

或者,您可以只计算字段的值,并将其作为第二个参数传递给电子服务上的流程方法。您的最终选择是按原样发送实体,并让接收服务计算“清晰”的表示。

但是,您还有其他问题。如果您转换到spring数据jpa而不是编写自己的存储库类,您可能会意识到它们是什么。简单地说,repository类旨在存储和检索某种持久存储中的实体,而不是调用其他服务。这将是最初使用回购协议的任何代码的责任。在spring mvc应用程序中,这通常是一个控制器,用于编排应用程序服务。

简单地说,您不应该从repo类中调用服务。它只能在实体上执行CRUD——绝对不能做其他任何事情。如果您需要它做其他事情,那么您可能做错了其他事情,或者您为您的应用程序选择了错误的架构。

您应该看看spring数据jpa。默认情况下,它配置为在运行时自动生成回购的实现。这可能会减少您的代码库,为您节省大量时间和缺陷,并且不会轻易让您犯此类架构错误。最重要的是,这比你现在做的要容易得多。

 类似资料:
  • 我只需要一个查询的临时结果。如果substring condition为true,我希望将其car_performance值降低%10(car_performance=car_performance*0.9;)并将此子字符串true cars与其他子字符串进行比较,但仅针对查询,我不想更改SQL数据库中的真实数据。 所以我想我需要将这个数据放到一个新的临时表中,这个表的car_performanc

  • 《铁锈之书》第3.5章摘录: 我们使用关键字和值。循环结束后,我们使用分号结束将值赋给的语句。 加上代码片段: 我理解这是如何工作的,以及为什么结果是20,但我注意到,如果我删除包含关键字的行上的分号,程序是等效的。 为什么分号在这种情况下是可选的?

  • 对于每个客户机,我们自然需要发送关于事务的反馈(OK或Exception->rollback)。 我的问题是:如果我使用,是否意味着只使用一个事务,如果第100个客户机遇到问题,第1个客户机的事务也会回滚?

  • 我知道自动布局链基本上由3个不同的过程组成。 更新约束 布局视图(这里是我们计算框架的地方) 显示 我并不完全清楚的是 和 之间的内在区别。来自苹果文档: 设置需要布局 当您想要调整视图子视图的布局时,请在应用程序的主线程上调用此方法。此方法记下请求并立即返回。由于此方法不会强制立即更新,而是等待下一个更新周期,因此您可以使用它来使多个视图的布局失效,然后再更新这些视图中的任何一个。此行为允许您将

  • 任务完成事件 function onFinish(\swoole_server $server, $taskId,$callBackObj); 当worker进程投递的任务在task_worker中完成时将触发本事件 task进程的onTask事件中没有调用finish方法或者return结果,worker进程不会触发onFinish 执行onFinish逻辑的worker进程与下发task任务的