2016-11-18 2 views
3

Sur mon projet, j'utilise Groovy avec les données Specification de Spring Data JPA pour construire des requêtes Hibernate.Avec les critères JPA, comment extraire une entité enfant d'une entité jointe sans extraire l'entité jointe?

Je ne peux pas fournir mes requêtes réelles, mais pour illustrer mon problème, disons que j'ai des entités de construction, et que chaque bâtiment a des étages et que chaque étage a des pièces et des fenêtres.

Le comportement que je tente de simuler est quelque chose comme cette requête SQL natif:

SELECT b.*, r.* 
FROM building b 
INNER JOIN floor f ON b.id = f.building_id 
INNER JOIN window w ON f.id = w.floor_id 
LEFT OUTER JOIN room r ON f.id = r.floor_id 
WHERE w.id = 1; 

J'ai une spécification similaire à ce qui suit:

public class MySpec implements Specification<Building> { 
    @Override 
    public Predicate toPredicate(final Root<Building> root, final CriteriaQuery<?> query, final CriteriaBuilder cb) { 
     final Join floorsJoin = root.join("floors"); 
     final Join windowsJoin = floorsJoin.join("windows"); 

     //I'd like to remove this line 
     final Fetch floorsFetch = root.fetch("floors"); // <--- 

     floorsFetch.fetch("rooms", JoinType.LEFT); 

     cb.equal(windowsJoin.get("id"), 1L); 
    } 
} 

La ligne annotée ci-dessus est mon problème . Si je laisse, la requête générée ressemble à quelque chose comme ceci:

SELECT b.*, f2.*, r.* 
FROM building b 
INNER JOIN floor f ON b.id = f.building_id 
INNER JOIN window w ON f.id = w.floor_id 
INNER JOIN floor f2 ON b.id = f2.building_id 
LEFT OUTER JOIN room r ON f2.id = r.floor_id 
WHERE w.id = 1; 

(notez le double INNER JOIN de floor et les données f2.* non nécessaires)

Si je le supprimer, et utiliser le floorsJoin au lieu de chercher des chambres , je reçois l'erreur Hibernate suivante:

org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list 

les f2.* inutiles données seraient OK sauf que je ne peux pas remplacer le floorsJoin ci-dessus avec t il floorsFetch parce que j'ai besoin de rejoindre la table windows (sans aller chercher windows) et la classe Fetch n'a pas de méthode .join.

J'ai du mal à comprendre comment j'accomplirais ce dont j'ai besoin tout en générant une seule requête; Je dois sûrement manquer quelque chose de simple. Toutes les pensées ou les conseils que vous pourriez fournir seraient grandement appréciés.

Merci beaucoup, B.J.

Répondre

0

Eh bien, ce n'est pas aussi simple que cela avec l'API de critères JPA. Avec Hibernate, vous pouvez simplement lancer le Fetch sur un Join Je suppose que cela ne vous aidera pas beaucoup. Je ne suis pas sûr comment vous utilisez la spécification dans ce cas, mais si vous pouvez écrire la requête dans son ensemble, il pourrait ressembler à ce qui suit

CriteriaBuilder cb = entityManager.getCriteriaBuilder(); 
CriteriaQuery<Tuple> cq = cb.createTupleQuery(); 

Root<Building> root = cq.from(Building.class); 
final Join floorsJoin = root.join("floors"); 
final Join windowsJoin = floorsJoin.join("windows"); 
final Join roomsJoin = floorsJoin.join("rooms", JoinType.LEFT); 

cb.equal(windowsJoin.get("id"), 1L); 

cq.multiselect(
    root, 
    roomsJoin 
);