【问题标题】:JPA Criteria API - Setup Joins and Fetch data from Joined EntityJPA Criteria API - 设置联接并从联接实体获取数据
【发布时间】:2015-07-22 13:45:18
【问题描述】:

我有两个实体FooFooDerivedData,如下所述。

实体 Foo:

@Entity
@Table(name="FOO")
public class Foo {

// skipping property declarations

@OneToOne
@PrimaryKeyJoinColumn(name = "FOO_ID")
@Fetch(FetchMode.JOIN)
public getFooDerivedData() {
    return fooDerivedData;
}

实体 FooDerivedData

@Entity
@Table(name = "FOO_DERIVED_VW")
@Immutable
public class FooDerivedData {

    @Id
    @Column(name = "FOO_ID")
    long fooId;

    @Column(name = "FOO_COUNT")
    private Integer fooCount;

    @Column(name = "FOO_SUM")
    private BigDecimal fooSum;


    // Getters left out here

我正在使用 JPA Criteria API(Hibernate 是 JPA 实现),我需要将 FooDerivedData 的实例加载到 Foo 中。默认情况下会急切地获取它,但我希望使用内部连接来获取它,而不是为FooDerivedData 的每个实例使用单独的选择语句。我成功地添加了针对 FooDerivedData 的内部连接。

CriteriaQuery<Foo> criteriaQuery = criteriaBuilder.createQuery(Foo.class);
Root<Foo> root = criteriaQuery.from(Foo.class);
root.join("fooDerivedData");
Predicate p = getPredicate(); // details omitted here
criteriaQuery.select(root).where(p);

虽然我可以从日志中的 SQL 看到为 FOO_DERIVED_VW 添加了内部连接,但没有选择任何列。对于Foo 的每个实例,我仍然需要有一个单独的选择语句来获取我随时可用的数据。

目前的查询是这样的:

select f.foo_id, f.foo_bar from from FOO f
inner join FOO_DERIVED_VW d on d.foo_id = f.foo_id;
select d.foo_id, d.foo_count, d.foo_sum from FOO_DERIVED_VW d where foo_id = ?;
select d.foo_id, d.foo_count, d.foo_sum from FOO_DERIVED_VW d where foo_id = ?;

请注意,我没有从加入的 FOO_DERIVED_VW 中选择任何数据,并且这些数据正在通过其他数据库命中来实现。我希望查询是这样的:

select f.foo_id, f.foo_bar, d.foo_count, d.foo_sum from from FOO f
inner join FOO_DERIVED_VW d on d.foo_id = f.foo_id;

如何从 Criteria API 中的连接实体中选择属性?我可以指示 javax.persistence.criteria.Root 选择我要加入的属性吗?

我尝试将连接更改为提取,或者我指定了连接类型的提取。无论哪种情况,它都会因 Hibernate QueryException 而失败。

Root<Foo> root = criteriaQuery.from(Foo.class);
root.fetch("fooDerivedData");

引起:org.hibernate.QueryException:查询指定连接 正在获取,但获取的关联的所有者不存在 选择列表

【问题讨论】:

    标签: java hibernate jpa criteria criteria-api


    【解决方案1】:

    我认为您在这里有几个选择。如果您的 where 条件不使用连接表中的任何列,那么您尝试的 fetch 应该如下工作:

    CriteriaQuery<Foo> criteriaQuery = criteriaBuilder.createQuery(Foo.class);
    Root<Foo> root = criteriaQuery.from(Foo.class);
    root.fetch("fooDerivedData");
    Predicate p = getPredicate(); // details omitted here
    criteriaQuery.select(root).where(p);
    

    如果您的谓词确实包含连接表中的列,那么您可以按照您的方式连接(获取目前不适用于此),然后还可以通过将其添加到您的内容中来指定动态实体图(从 JPA 2.1 开始)有:

    CriteriaQuery<Foo> criteriaQuery = criteriaBuilder.createQuery(Foo.class);
    Root<Foo> root = criteriaQuery.from(Foo.class);
    root.join("fooDerivedData");
    Predicate p = getPredicate(); // details omitted here
    criteriaQuery.select(root).where(p);
    EntityGraph<Foo> fetchGraph = entityManager.createEntityGraph(Foo.class);
    fetchGraph.addSubgraph("fooDerivedData");
    EntityManager.createQuery(criteriaQuery).setHint("javax.persistence.loadgraph", fetchGraph);
    

    【讨论】:

      【解决方案2】:

      我相信您不需要为 getFooDerivedData() 指定 FetchMode,因为 OneToOne 关系的默认获取类型是 EAGER。希望这会有所帮助!

      【讨论】:

      • 感谢您的回复。急切地获取数据不是问题。有效地获取它是我所追求的。就目前而言,加入 FooDerivedData 没有任何好处,因为我不在那里获取数据。
      • 如果您担心实体的性能和获取图,请将 FooDerivedData 标记为 FETCH=LAZY。您可以使用以下任何选项来避免 FooDerivedData 的任何延迟初始化异常。 1. 删除 getter 方法 - 在此选项中,您需要将 FooDerivedData 作为字段添加到实体类中。移除 getter 方法并提供服务方法来获取 FooDerivedData。 2. JPA 提取图 - 如果您使用 JPA 2.1 + 版本,请考虑为您的实体提供提取图。 3. jpql/HQL/Criteria 中的 FETCH JOIN - 不言自明。我更喜欢选项 1,因为它更安全!
      • 抱歉,没有足够的字符用于 cmets,所以这里是我评论的第 2 部分。如果获取模式是 EAGER,那么无论您是否使用,hibernate 都会始终查询关联的实体。您可以通过启用 hibernate.show_sql 属性来检查这一点。如果您真的担心性能,请考虑上述选项之一。
      • 谢谢,我熟悉核心抓取策略以及如何使用延迟抓取策略来提高性能。我的问题是在您的评论中使用 CriteriaQuery 中的 fetch join 的选项 #3 的上下文中提出的。根据我对原始 SQL 的审查,我没有通过连接获取 FooDerivedData,而是从单独的选择语句中获取它。原始 SQL 显示我能够添加内部联接,但我只是没有选择我需要的列。我只想调整我对 CriteriaQuery 的使用,以便从我加入的表/视图中实际选择数据。
      • 所以回到最初的问题,根据 Hibernate 文档,“一个“获取”连接允许关联或值的集合与它们的父对象一起被初始化”。这当然是不可能的,因为“foodDeliveredData”不是选定的字段之一,所以例外是完全正确的。请转到docs.jboss.org/hibernate/orm/3.6/reference/en-US/html/… 并搜索我引用的文字。在您的情况下,最简单的解决方案是从条件查询中删除 fetch join 并将 OneToOne 关系保持为 EAGER。
      猜你喜欢
      • 1970-01-01
      • 2018-12-24
      • 1970-01-01
      • 2018-09-18
      • 2020-04-26
      • 2018-07-04
      • 2012-02-03
      • 2013-06-10
      • 1970-01-01
      相关资源
      最近更新 更多