2

I'm working with JPA CriteriaBuilder, CriteriaQuery etc. and static metamodels for typesafe queries, like here for example: click.

I have 3 tables: Client, Package, Vegetable.

Every client has 1 or more packages, and those packages contain multiple vegetables.

What I have now:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Client> query = builder.createQuery(Client.class);
Root<Client> root = query.from(Client.class);
ListJoin<Package, Vegetable> join = root.join(Client_.packages).join(Package_.vegetables);
TypedQuery<Client> typedQuery = entityManager.createQuery(query);
return typedQuery.getResultList();

The ListJoin I added recently. Point is what Hibernate does: generates the whole select for all fields in Client class from the Client table inner joined with Package and Vegetable, but it doesn't actually selects those fields from joined tables. It gets every package by ID and then every vegetable by ID, thus doing n+1 selects.

Without the ListJoin it doesn't inner join those tables, but I'm working on it right now so I added those joins. Now I want to select all the fields from those classes so I get whole object hierarchy with 1 select. I tried doing something with projections like in the link I gave in Projecting the result chaper, but I have no idea how to connect that with ListJoin.

Is that even possible in this situation? When I run this query on database (with manually added all the fields from joined tables) it works fine, but would Hibernate handle that? And if so - how to project the result so it selects all the fields from 3 tables joined together and constructs whole object hierarchy, all with 1 select?

//Edit: managed to retrieve all packages with a single query, but going further raises exception:

Root<Client> root = query.from(Root.class);

ListJoin<Client, Package> join = root.join(Client_.packages);
ListJoin<Package, Vegetable> secondJoin = join.join(Package_.vegetables);

root.fetch(Client_.packages);

Naturally, I tried to add: join.fetch(Package_.vegetables); but it raises the org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list, no idea what is that.

As to the latest comment: gonna try that now.

//Edit2: I added 2 fetches (couldn't cast them to Join like in that answer, compiler errors):

Fetch<Client, Package> join = root.fetch(Client_.packages);
Fetch<Package, Vegetable> secondJoin = join.fetch(Package_.vegetables);

It raises org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags, known error I guess so at least got something to search for.

//Edit3: Changed it both to Sets and it works, thanks, couldn't have done it without the Fetch instead of Join suggestion, seems kinda unintuitive to me.

Shadov
  • 5,421
  • 2
  • 19
  • 38
  • You need to use root.fetch(...) instead of root.join(...), see https://stackoverflow.com/questions/17306655/using-the-jpa-criteria-api-can-you-do-a-fetch-join-that-results-in-only-one-joi. – ewramner Jun 02 '17 at 13:16
  • 1
    Couold you please write out the final solution you used? I've been looking for this solution FOREVER. I'd rephrase the title to get more hits for people who need to to something like, "JPA CriteriaQuery Deep And Nested Joins For OnetoMany Relationships". – Michael Rountree Dec 27 '19 at 22:15

0 Answers0