【问题标题】:When does Spring Data JPA actually call INSERT on the database?Spring Data JPA 何时在数据库上实际调用 INSERT?
【发布时间】:2014-01-05 00:15:06
【问题描述】:

当您使用 JPARepository/Hibernate 保存对象时,它首先检查它是否存在(使用主键)在数据库中,然后插入。所以我们看到 2 个日志:SELECT & INSERT。

这就是我试图通过弹簧数据做的事情:(X 和 XY 是 2 个不同的对象)

Call Save on XRepository for Object X1

Call Save on XYRepository for Object X1Y1
Call Save on XYRepository for Object X1Y2
Call Save on XYRepository for Object X1Y3

Call Save on XRepository for Object X2

Call Save on XYRepository for Object X2Y1
Call Save on XYRepository for Object X2Y2
Call Save on XYRepository for Object X2Y3

这是我在日志中的观察结果:

SELECT X1
INSERT X1
SELECT X1Y1
SELECT X1Y2
SELECT X1Y3
INSERT X1Y1
INSERT X1Y2
INSERT X1Y3
SELECT X2
INSERT X2
SELECT X2Y1
SELECT X2Y2
SELECT X2Y3
INSERT X2Y1
INSERT X2Y2
INSERT X2Y3

那么 Spring Data 是什么时候真正调用插入的呢?这是如何工作的?

这是我所期望的:

SELECT X1
INSERT X1
SELECT X1Y1
INSERT X1Y1
SELECT X1Y2
INSERT X1Y2
SELECT X1Y3
INSERT X1Y3
...

【问题讨论】:

  • 这些实体是如何映射的?他们是否使用生成的 ID?
  • X 有一个生成的 ID。第三个对象 Z 具有生成的 ID。 XY 有一个由 X 的 ID 和 Z 的 ID 组成的复合键。我刚刚测试并发现了另一个我想分享的有趣的东西:对于 XY,如果我删除复合主键(通过 IdClass)并添加第三个属性,即生成的 ID,它的行为符合我的预期(即一起获取选择/插入日志)
  • saveAndFlush 也使它按预期工作。嗯。

标签: java spring jpa spring-data spring-data-jpa


【解决方案1】:

默认情况下,Hibernate 在刷新期间插入保存的实体。它解释了为什么XYs 被批量插入(在刷新期间),以及为什么saveAndFlush() 立即插入它们。

但是在插入期间由数据库生成主键的实体(即生成主键并且生成策略需要实际插入才能生成键)会立即插入,因为 Hibernate 应该知道已保存实体的 id。这就是你观察到的X

【讨论】:

    【解决方案2】:

    Spring Data JPA 本身并不直接控制与数据库的交互。它所做的只是与EntityManager 进行交互,因此所有行为效果都由 JPA 和底层 OR-mapper 定义。

    持久性提供程序通常会尝试批量处理数据库交互,因为这样可以最大限度地减少开销,尤其是当您在单个会话中执行大量持久性操作时。所以通常EntityManager 只会在以下情况之一发生时才将更改刷新到磁盘:

    JPA 如何处理这个

    1. EntityManager(读作:Hibernate 情况下的Session)被关闭,脏检查机制发现挂起的更改。因此,您可能已经使用 ….persist(…) 更改了附加实体的属性或将其添加到持久性上下文中。
    2. 要持久化的实体使用 ID 生成机制。
    3. 您手动调用 flush 方法。
    4. 您触发了一个查询,而EntityManager 有待处理的更改。然后它将这些刷新到数据库以确保查询可能已经看到更改的数据。

    顺便说一句:最后一点有时会导致奇怪的效果 DataIntegrityViolationExceptions 在查询时被抛出 被处决了,很多人一开始就没有明白这一点。但 这是在查询执行之前刷新待处理的更改 这会导致问题。

    这导致了一些重要的观察:

    Spring 数据细节

    1. 对于非 ID 自动生成的实体,不要期望它们会在 EntityManager 关闭之前出现在数据库中。 对于 Spring,这通常绑定到事务的生命周期,所以你可能需要先提交并检查已更改对象的更改属性。
    2. 您可以查询它并检查返回的对象,而不是直接检查对象。 这使用了我在上面 4 中描述的内容。注意:这种方法不要使用findOne(…),因为这将使用EntityManager.find(type, id)-方法,该方法通常大量使用一级缓存(可能没有先刷新)。
    3. 作为最后的手段,您可以在JpaRepository 接口上使用saveAndFlush(…),但我们强烈建议不要这样做,因为您可以有效地将持久层内部结构暴露给客户端。更喜欢使用自动 ID 生成的实体。

    【讨论】:

      猜你喜欢
      • 2019-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-21
      • 2021-03-05
      • 1970-01-01
      • 2022-10-21
      • 1970-01-01
      相关资源
      最近更新 更多