Sorting and paging with Hibernate's Criteria api comes with a big restriction, it needs to retrieve distinct results from the database. Tools provided by the api like DistinctRootTransformer won't work, because it is applied after retrieving the entities from db and thus breaks paging and sorting. The only way to get distinct results of a query with restrictions on an association is by limiting the resultset by a DetachedCriteria:
DetachedCriteria dc = DetachedCriteria.forClass(Household.class, "h")
.createAlias("cats", "c", JoinType.LEFT_OUTER_JOIN)
.add(Restrictions.or(
Restrictions.isEmpty("cats"),
Restrictions.ne("c.name", "Sylvester")
))
.setProjection(Projections.distinct(Projections.property("h.id")));
Criteria criteria = session.createCriteria(Household.class)
.add(Property.forName("id").in(dc));
...apply paging, sorting and filtering to criteria.
Does anybody know a better approach such as omitting subqueries and use joins without breaking pagination? My goal is to find a solution that is reusable, like passing only a criteria to another method that applies paging, sorting and filtering.
Update:
The following code does not work. Because of the join I have to use a Resulttransformer, to get distinct results. However, it is applied after sorting and paging.
Criteria criteria = session.createCriteria(Household.class)
.createAlias("cats", "c", JoinType.LEFT_OUTER_JOIN)
.add(Restrictions.ne("c.name","Sylvester"))
.setFirstResult((page - 1) * pagesize)
.setMaxResults(pagesize)
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
E.g. debugging the sql the database would return something like that:
household_id=1,...cat_ids={1,2};household_id=1,...cat_ids={1,2};household_id=2,...cat_ids={1};
In this example, setting pagesize to 1 and viewing page 2 should return the uid 2, because there are only two distinct users. But as you can see in the database output, it returns the wrong uid 1, because Resulttransformers kicks in afterwards.