70 votes

Comment obtenir des résultats distincts dans Hibernate avec des jointures et une limitation par ligne (pagination) ?

J'essaie de mettre en œuvre la pagination en utilisant la limitation par rangée (par exemple : setFirstResult(5) y setMaxResults(10) ) sur une requête Hibernate Criteria qui comporte des jointures avec d'autres tables.

Il est compréhensible que les données soient coupées de manière aléatoire, et la raison en est expliquée. ici .

Comme solution, la page suggère d'utiliser un "second sql select" au lieu d'une jointure.

Comment puis-je convertir ma requête de critères existante (qui comporte des jointures utilisant la fonction createAlias() ) pour utiliser une sélection imbriquée à la place ?

107voto

Vous pouvez obtenir le résultat souhaité en demandant une liste d'identifiants distincts au lieu d'une liste d'objets hydratés distincts.

Il suffit de l'ajouter à vos critères :

criteria.setProjection(Projections.distinct(Projections.property("id")));

Vous obtiendrez désormais le nombre correct de résultats en fonction de votre limitation par rangée. La raison pour laquelle cela fonctionne est que la projection effectuera le contrôle de distinction dans le cadre de la requête sql, au lieu de ce que fait un ResultTransformer qui est de filtrer les résultats pour la distinction après la requête sql a été effectuée.

Il est intéressant de noter qu'au lieu d'obtenir une liste d'objets, vous obtiendrez désormais une liste d'identifiants, que vous pourrez utiliser ultérieurement pour hydrater des objets depuis Hibernate.

43voto

grayshop Points 261

J'utilise celui-ci avec mes codes.

Il suffit de l'ajouter à vos critères :

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) ;

ce code sera comme le select distinct * from table du sql natif. J'espère que cela vous aidera.

27voto

Daniel Alexiuc Points 2304

Une légère amélioration qui s'appuie sur la suggestion de FishBoy.

Il est possible d'effectuer ce type de requête en une seule fois, plutôt qu'en deux étapes distinctes. Ainsi, la requête unique ci-dessous affichera correctement des résultats distincts, et renverra également des entités au lieu de simples ID.

Il suffit d'utiliser un DetachedCriteria avec une projection d'id comme sous-requête, puis d'ajouter des valeurs de pagination sur l'objet Critères principal.

Cela ressemblera à quelque chose comme ceci :

DetachedCriteria idsOnlyCriteria = DetachedCriteria.forClass(MyClass.class);
//add other joins and query params here
idsOnlyCriteria.setProjection(Projections.distinct(Projections.id()));

Criteria criteria = getSession().createCriteria(myClass);
criteria.add(Subqueries.propertyIn("id", idsOnlyCriteria));
criteria.setFirstResult(0).setMaxResults(50);
return criteria.list();

6voto

nikita Points 156

Une petite amélioration à la suggestion de @FishBoy est d'utiliser la projection id, de sorte que vous n'ayez pas à coder en dur le nom de la propriété de l'identifiant.

criteria.setProjection(Projections.distinct(Projections.id()));

5voto

JJ. Points 19

La solution :

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

fonctionne très bien.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X