【问题标题】:Hibernate - FetchType.LAZY and FetchType.EAGER ignoredHibernate - FetchType.LAZY 和 FetchType.EAGER 被忽略
【发布时间】:2020-01-04 20:46:33
【问题描述】:

我有一个实体(名为 Parent),其 @OneToOne 映射到一个子实体。目前使用 FetchType.EAGER 定义,但与这里的问题无关。

我正在尝试对 Parent 执行查询,该查询在 Child 实体上执行 LEFT JOIN,从而使 FetchType 设置无用(据说)。 但是,即使连接正确执行,对子实体的查询仍会执行。

@Entity
public class Parent {
    @NotFound(action = NotFoundAction.IGNORE)
    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "id", insertable = false, updatable = false)
    private Child child;
}

@Entity
public class Child {
}

CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
        CriteriaQuery<Parent> criteriaQuery = criteriaBuilder.createQuery(Parent.class);

Root<Parent> root = criteriaQuery.from(Parent.class);
Fetch<Parent, Child> join = root.fetch("child", JoinType.LEFT);

return session.createQuery(criteriaQuery).getSingleResult();

我原以为这会生成一个带有 LEFT JOIN 的查询,该查询会收集所有数据,但它却执行了:

1) SELECT * FROM parent LEFT JOIN child ...
2) SELECT * FROM child where id = ?

【问题讨论】:

  • @NotFound(action = NotFoundAction.IGNORE) 是性能不佳和数据不一致的最佳方式。修复您的数据,删除该注释,并添加一个外键约束。我敢打赌,问题会消失。
  • 在这种情况下,我得到: javax.persistence.EntityNotFoundException:不存在具有给定标识符的行 @OneToOne 映射是可选的 - 如果行存在于 Child 中,则它必须存在于 Parent 中,但不存在于另一个绕路。在这种情况下,我本来希望 Child 对象填充为 NULL ......另外 - 附加查询仍然被执行......
  • 啊,好吧,我错过了您将 ID 用作子实体的外键这一事实。您应该使用单独的 child_id 列而不是使用 ID。事实上,在使用 ID 时,Hibernate 无法知道父母是否有孩子。如果您使用单独的列,则只需测试 child_id 是否为空。

标签: java hibernate jpa criteria-api


【解决方案1】:

实际上,您的条件会生成正确的查询 (1)。但是当 Parent 实体被具体化时,Hibernate 会发出 (2) 查询,因为 Child 应该被急切地加载。换句话说,Hibernate 总是会尝试加载孩子。

有两种方法可以避免这种情况:

  • 移除 EAGER 提取。
  • 更改标准以返回投影

【讨论】:

    【解决方案2】:

    我认为您应该在子字段上添加注释@Fetch(FetchMode.JOIN)。 JoinType 只是定义了连接的类型,但你应该告诉 hibernate 在加载父节点时,使用连接加载子节点(这里是急切地),而不是使用额外的选择。

    这种方式你不需要在条件查询中指定连接,你可以只加载父实体,休眠将使用 FetchMode.JOIN 加载子实体

    【讨论】:

      【解决方案3】:

      好的,最终解决方案是建立双向关联。定义两个方向为我解决了这个问题 - 就像这样:

      @Entity
      public class Parent {
          @OneToOne(mappedBy="parent")
          @Fetch(FetchMode.JOIN)
          private Child child;
      }
      
      @Entity
      public class Child {
          @OneToOne(fetch = FetchType.LAZY)
          @JoinColumn(name = "id", insertable = false, updatable = false)
          private Parent parent;
      }
      

      感谢您的帮助:)

      【讨论】:

        猜你喜欢
        • 2021-09-05
        • 1970-01-01
        • 2013-07-24
        • 2020-03-12
        • 2023-03-21
        • 2013-04-24
        • 2020-02-02
        • 2020-11-02
        • 2017-12-05
        相关资源
        最近更新 更多