JPA 2 Criteria Fetch Path Navigation
Asked Answered
B

8

34

With JPA 2 Criteria Join method I can do the following:

    //Join Example (default inner join)
    int age = 25;
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Team> c = cb.createQuery(Team.class);
    Root<Team> t = c.from(Team.class);
    Join<Team, Player> p = t.join(Team_.players);
    c.select(t).where(cb.equal(p.get(Player_.age), age));
    TypedQuery<Team> q = entityManager.createQuery(c);
    List<Team> result = q.getResultList();

How can I do the same with fetch method, I expected that Fetch interface had get method for path navigation but it doesn't:

    //Fetch Join Example

    int age = 25;
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Team> cq = cb.createQuery(Team.class);
    Root<Team> t = cq.from(Team.class);
    Fetch<Team,Player> p = t.fetch(Team_.players);
    cq.where(cb.equal(p.get(Player_.age), age)); //This leads to compilation error there is no such method get in interface Fetch
    TypedQuery<Team> q = entityManager.createQuery(cq);
    List<Team> result = q.getResultList();

According to Hiberante Documentation fetch returns a Join object which is wrong. http://docs.jboss.org/hibernate/stable/entitymanager/reference/en/html/querycriteria.html#querycriteria-from-fetch

Barbaraanne answered 22/12, 2010 at 16:36 Comment(1)
I have the same problem? Have you found some workarounds?Thirtytwomo
L
24

Agree with you about that method, and the fact that you would expect it to allow what you say. Another option would be

Join<Team, Player> p = t.join(Team_.players);
t.fetch(Team_.players);
c.select(t).where(cb.equal(p.get(Player_.age), age));

i.e do a join(), add a fetch() for it, and then make use of the join. This is illogical and only adds to the inelegant nature of JPA Criteria, but anyway, ought to be a workaround

Linehan answered 22/12, 2010 at 19:12 Comment(5)
Apparently it works but it leads to joining multiple times: Hibernate: select ... from Team team0_ inner join Player players1_ on team0_.id=players1_.team_id inner join Person players1_1_ on players1_.id=players1_1_.id inner join Player players2_ on team0_.id=players2_.team_id inner join Person players2_1_ on players2_.id=players2_1_.id where players1_1_.age=25Barbaraanne
See also blogs.sun.com/ldemichiel/entry/… where the problem is mentioned. Basically an API that isn't thought outLinehan
This breaks when you do a order by and distinct(true) with mssqlCorespondent
As Alfredo says, this does double join.Disprize
the blogs.sun.com link above is done, it can be found on the wayback machine at web.archive.org/web/20110820095223/http://blogs.oracle.com:80/…Indra
C
12

It Works for me using Hibernate Provider.

//Join Example (default inner join)

    int age = 25;
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Team> c = cb.createQuery(Team.class);
    Root<Team> t = c.from(Team.class);

    // Join<Team, Player> p = t.join(Team_.players); 
    Join<Team, Player> p = (Join<Team, Player>)t.fetch(Team_.players); 

    c.select(t).where(cb.equal(p.get(Player_.age), age));
    TypedQuery<Team> q = entityManager.createQuery(c);
    List<Team> result = q.getResultList();

Certainly, it could broken the portability, but in our case we have been using others hibernate's exclusive features.

*It is very strange because the hibernate documentation doesn't show this example.

To grasp it look at this interface.

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
 * indicated by the @author tags or express copyright attribution
 * statements applied by the authors.  All third-party contributions are
 * distributed under license by Red Hat Inc.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.hibernate.ejb.criteria;

import javax.persistence.criteria.Fetch;
import javax.persistence.criteria.Join;

/**
 * Consolidates the {@link Join} and {@link Fetch} hierarchies since that is how we implement them.
 * This allows us to treat them polymorphically.
*
* @author Steve Ebersole
*/
public interface JoinImplementor<Z,X> extends Join<Z,X>, Fetch<Z,X>, FromImplementor<Z,X> {
    /**
     * {@inheritDoc}
     * <p/>
     * Refined return type
     */
    public JoinImplementor<Z,X> correlateTo(CriteriaSubqueryImpl subquery);
}
Concierge answered 28/6, 2012 at 14:16 Comment(5)
Nice. As you said it is possible that this will make it not portable across other JPA vendors.Barbaraanne
The cast didn't work for me, using WAS v8.0's OpenJPA 2.1.2-SNAPSHOT. :-(Botheration
This cast doesn't work. What Hibernate version are you using?Thirtytwomo
In this specific case i was using : hibernate 3.6.9.Final, i will test it with a new version and comment here. what version are u using @amgohan ?Concierge
Works great but I have nullpointer when getting fields in second levels (child tables) so I have to use String names for fields instead of those in the metamodel, ¿Anybody with the same problem?Blemish
T
6

Beginning with JPA 2.1, dynamic entity graphs can be used for fetching in criteria queries, while using join() instead of fetch(). From the example in the question:

//Join Example (default inner join)
int age = 25;
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Team> c = cb.createQuery(Team.class);
Root<Team> t = c.from(Team.class);
Join<Team, Player> p = t.join(Team_.players);
c.select(t).where(cb.equal(p.get(Player_.age), age));
TypedQuery<Team> q = entityManager.createQuery(c);
List<Team> result = q.getResultList();

If this:

TypedQuery<Team> q = entityManager.createQuery(c);

is replaced with this:

EntityGraph<Team> fetchGraph = getEntityManager().createEntityGraph(Team.class);
fetchGraph.addSubgraph(Team_.players);
TypedQuery<Team> q = entityManager.createQuery(c).setHint("javax.persistence.loadgraph", fetchGraph);

then all players will be eager fetched.

Tanishatanitansy answered 23/3, 2017 at 18:21 Comment(0)
F
4

All you have to do is the following:

1- Do Fetch. 2- Then, go over the path to where you want.

In your case:

int age = 25;
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Team> cq = cb.createQuery(Team.class);
Root<Team> t = cq.from(Team.class);
Fetch<Team,Player> p = t.fetch(Team_.players);
cq.where(cb.equal(t.get("player").get("age"), age)); 
Francisco answered 15/10, 2015 at 22:6 Comment(1)
t.get("player") doesn't exist, as Team entity has only players attribute (also in our example is Team_.players). So I'll get java.lang.IllegalArgumentException: Unable to resolve attribute [player] against pathButanone
M
4

I am using JPA 2.1 with Hibernate 4.3.7 and the below works for me well. It does not even look that ugly.

Join<Team,Player> p = (Join) t.fetch(Team_.players);
Madrid answered 11/12, 2015 at 18:42 Comment(0)
A
1

ugly but:

Join<Team, Player> p=t.fetch(Team_.players);

will produce singel join with fetch in sql but is a ugly hack that works JBoss6.1 hibernate

Armet answered 6/4, 2012 at 21:3 Comment(0)
F
0

I updated the Hibernate Version to "5.6.5.Final" and I think the problem is resolved.

Flambeau answered 8/2, 2022 at 10:41 Comment(0)
S
0

I never figured out the way to help my IntelliJ IDE help me with the underscore thing (Player_) with the Entity class. If someone can show me the way that will be very helpful but from this question's answer point of view, I used the following to make things work:

    int age = 25;
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Team> cq = cb.createQuery(Team.class);
    Root<Team> t = cq.from(Team.class);

    Fetch<Team,Player> fetch = t.fetch("players");
    Join<Team,Player> p = (Join<Team,Player>) fetch;
    cq.select(t).where(cb.equal(p.get("age"), age));
    
    TypedQuery<Team> q = entityManager.createQuery(cq);
    List<Team> result = q.getResultList();
Schug answered 10/3, 2024 at 6:18 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.