使用JPA Criteria API,您是否可以执行只获得一次连接的获取连接?

时间:2013-06-25 20:13:08

标签: java jpa jpql criteria-api

使用JPA 2.0。似乎默认情况下(没有显式提取),在1 + N个查询中提取@OneToOne(fetch = FetchType.EAGER)个字段,其中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

唉,如果我只进行获取,那么我没有在where子句中使用的表达式。

我错过了什么,或者这是Criteria API的限制?如果是后者,这是否在JPA 2.1中得到纠正,还是有任何特定于供应商的增强功能?

否则,最好放弃编译时类型检查(我意识到我的示例不使用元模型)并使用动态JPQL TypedQueries。

3 个答案:

答案 0 :(得分:26)

您可以使用返回root.join(...)对象的root.fetch(...)而不是Fetch<>

Fetch<>Join<> 的后代,但它可以以类似的方式使用。

你只需要将Fetch<>转换为Join<>它应该适用于EclipseLink和Hibernate

...
Join<MyEntity, RelatedEntity> join = (Join<MyEntity, RelatedEntity>)root.fetch("relatedEntity");
...

答案 1 :(得分:4)

从JPA 2.1开始,您可以使用动态实体图表。删除您的提取并指定实体图表,如下所示:

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);

答案 2 :(得分:1)

在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");