使用 JPA Criteria API,你能做一个只导致一个连接的 fetch join 吗?
Posted
技术标签:
【中文标题】使用 JPA Criteria API,你能做一个只导致一个连接的 fetch join 吗?【英文标题】:Using the JPA Criteria API, can you do a fetch join that results in only one join? 【发布时间】:2013-06-22 19:12:28 【问题描述】:使用 JPA 2.0。似乎默认情况下(无显式获取),@OneToOne(fetch = FetchType.EAGER)
字段在 1 + N 个查询中获取,其中 N 是包含定义与不同相关实体的关系的实体的结果数。使用 Criteria API,我可能会尝试避免这种情况,如下所示:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<MyEntity> query = builder.createQuery(MyEntity.class);
Root<MyEntity> root = query.from(MyEntity.class);
Join<MyEntity, RelatedEntity> join = root.join("relatedEntity");
root.fetch("relatedEntity");
query.select(root).where(builder.equals(join.get("id"), 3));
理想情况下,上述内容应等效于以下内容:
SELECT m FROM MyEntity m JOIN FETCH myEntity.relatedEntity r WHERE r.id = 3
但是,条件查询导致根表不必要地连接到相关实体表两次;一次用于获取,一次用于 where 谓词。生成的 SQL 如下所示:
SELECT myentity.id, myentity.attribute, relatedentity2.id, relatedentity2.attribute
FROM my_entity myentity
INNER JOIN related_entity relatedentity1 ON myentity.related_id = relatedentity1.id
INNER JOIN related_entity relatedentity2 ON myentity.related_id = relatedentity2.id
WHERE relatedentity1.id = 3
唉,如果我只做 fetch,那么我就没有可在 where 子句中使用的表达式。
我是否遗漏了什么,或者这是 Criteria API 的限制?如果是后者,这是否在 JPA 2.1 中得到纠正,或者是否有任何特定于供应商的增强功能?
否则,最好放弃编译时类型检查(我意识到我的示例不使用元模型)并使用动态 JPQL TypedQueries。
【问题讨论】:
【参考方案1】:您可以使用返回Fetch<>
对象的root.fetch(...)
而不是root.join(...)
。
的后代,但可以以类似的方式使用。 Fetch<>
是Join<>
您只需要将 Fetch<>
转换为 Join<>
它应该适用于 EclipseLink 和 Hibernate
...
Join<MyEntity, RelatedEntity> join = (Join<MyEntity, RelatedEntity>)root.fetch("relatedEntity");
...
【讨论】:
Fetch 既不是 Join 也不是 Expression 的子接口,所以我不能将它分配给 Join,也不能在 where 子句中使用它(例如)。参考:docs.oracle.com/javaee/6/api/javax/persistence/criteria/… 那么,这怎么可能呢?它是特定于提供商的吗? 对不起,你应该投它。它至少应该适用于 Hybernate,如此处所述docs.jboss.org/hibernate/orm/4.0/hem/en-US/html/…我已经解决了我的答案。 这对我有用,但转换为Join<MyEntity, RelatedEntity>
导致我的 IDE 将该语句标记为警告。我发现投射到org.hibernate.ejb.criteria.path.SingularAttributeJoin<MyEntity, RelatedEntity>
使警告消失了。调用 root.fetch
会执行以下操作:FetchParent#fetch(String)
AbstractFromImpl#fetch(String) [Implementation of FetchParent]
AbstractFromImpl#fetch(SingularAttribute, JoinType)
AbstractFromImpl#constructJoin(SingularAttribute,JoinType) [Returns SingularAttributeJoin]
有点老话题,但也许你会知道:即使可以从Fetch
转换为Join
,你也不能这样做fetch.on(...)
,因为它会说with clause cannot be used with fetch join - use filters
.我需要添加一个额外的条件来加入(除了 ID)。当我只是做root.join(...)
然后join.on(...)
生成正确的连接,但它们没有获取连接数据(选择中没有列出字段)。也许您知道一些解决方法?
@ChrisParton 类型转换为 (Join)
让您的 IDE 开心!【参考方案2】:
从 JPA 2.1 开始,您可以为此使用动态实体图。删除您的 fetch 并指定一个实体图,如下所示:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<MyEntity> query = builder.createQuery(MyEntity.class);
Root<MyEntity> root = query.from(MyEntity.class);
Join<MyEntity, RelatedEntity> join = root.join("relatedEntity");
query.select(root).where(builder.equal(join.get("id"), 3));
EntityGraph<MyEntity> fetchGraph = entityManager.createEntityGraph(MyEntity.class);
fetchGraph.addSubgraph("relatedEntity");
entityManager.createQuery(query).setHint("javax.persistence.loadgraph", fetchGraph);
【讨论】:
CriteriaBuilder 的正确方法是等于不等于【参考方案3】:在 EclipseLink 上使用 root.fetch()
将创建一个带有 INNER JOIN
的 SQL,因为它有 3 种类型,默认为 INNER。
INNER, LEFT, RIGHT;
建议使用 CreateQuery。
TypedQuery<T> typedQuery = entityManager.createQuery(query);
编辑:您可以像这样将根转换为 From:
From<?, ?> join = (From<?, ?>) root.fetch("relatedEntity");
【讨论】:
问题是关于 CriteriaQuery 它没有 setHint 我解释了如何帮助使用另一种方法来解决。如果你不明白,我什么也做不了。 当您使用条件 API 构建查询时,您不会丢弃整个代码,而是使用 TypedQuery 构建查询,因此您可以设置提示 请参阅“使用 JPA 标准 API”问题的标题。您不妨建议如何仅使用 sql 来完成 我可以这样做 entityManager.createQuery(CriteriaQuery) 像这样!您可以使用:String、CriteriaQuery、CriteriaUpdate 和 CriteriaDelete。 阅读此 API docs.oracle.com/javaee/6/api/javax/persistence/criteria/… 你看到 setHint 了吗?以上是关于使用 JPA Criteria API,你能做一个只导致一个连接的 fetch join 吗?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 JPA Criteria API 连接不相关的实体
使用 JPA2 Criteria API 选择 MAX 时间戳
JPA 如何获取 Criteria API 中使用的参数标记的数量?