Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using the JPA Criteria API, can you do a fetch join that results in only one join?

Using JPA 2.0. It seems that by default (no explicit fetch), @OneToOne(fetch = FetchType.EAGER) fields are fetched in 1 + N queries, where N is the number of results containing an Entity that defines the relationship to a distinct related entity. Using the Criteria API, I might try to avoid that as follows:

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

The above should ideally be equivalent to the following:

SELECT m FROM MyEntity m JOIN FETCH myEntity.relatedEntity r WHERE r.id = 3 

However, the criteria query results in the root table needlessly being joined to the related entity table twice; once for the fetch, and once for the where predicate. The resulting SQL looks something like this:

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 

Alas, if I only do the fetch, then I don't have an expression to use in the where clause.

Am I missing something, or is this a limitation of the Criteria API? If it's the latter, is this being remedied in JPA 2.1 or are there any vendor-specific enhancements?

Otherwise, it seems better to just give up compile-time type checking (I realize my example doesn't use the metamodel) and use dynamic JPQL TypedQueries.

like image 447
Shaun Avatar asked Jun 25 '13 20:06

Shaun


People also ask

What is Criteria API in JPA?

The Criteria API is a predefined API used to define queries for entities. It is the alternative way of defining a JPQL query. These queries are type-safe, and portable and easy to modify by changing the syntax. Similar to JPQL it follows abstract schema (easy to edit schema) and embedded objects.

How do you write join query in Hibernate using criteria?

Criteria in Hibernate can be used for join queries by joining multiple tables, useful methods for Hibernate criteria join are createAlias(), setFetchMode() and setProjection() Criteria in Hibernate API can be used for fetching results with conditions, useful methods are add() where we can add Restrictions.

How do you join unrelated entities with JPA and Hibernate?

The only way to join two unrelated entities with JPA 2.1 and Hibernate versions older than 5.1, is to create a cross join and reduce the cartesian product in the WHERE statement. This is harder to read and does not support outer joins. Hibernate 5.1 introduced explicit joins on unrelated entities.


2 Answers

Instead of root.join(...) you can use root.fetch(...) which returns Fetch<> object.

Fetch<> is descendant of Join<> but it can be used in similar manner.

You just need to cast Fetch<> to Join<> it should work for EclipseLink and Hibernate

... Join<MyEntity, RelatedEntity> join = (Join<MyEntity, RelatedEntity>)root.fetch("relatedEntity"); ... 
like image 148
Ondrej Bozek Avatar answered Oct 10 '22 11:10

Ondrej Bozek


Starting with JPA 2.1 you can use dynamic entity graphs for this. Remove your fetch and specify an entity graph as follows:

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); 
like image 28
Darren Reimer Avatar answered Oct 10 '22 11:10

Darren Reimer