Is there a better way than using DetachedCriteria for pagination?

时间:2015-11-12 11:08:01

标签: java hibernate sorting hibernate-criteria data-paging

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.

2 个答案:

答案 0 :(得分:1)

When I need to paginate, I never use DetachedCriteria, but instead normal Criteria, controlling first and max results.

From view I determine which page I need to show and once I have prepared the critera I configure this way:

criteria.setMaxResults(lazyQuery.getPageSize());
criteria.setFirstResult(layQuery.getStart());

lazyQuery is an object of my own model, used for view and business logic. This works perfect.

答案 1 :(得分:1)

如果您在某些方法中创建DetachedCriteria然后使用它,您可以尝试使用以下内容将DetachedCriteria转换为Criteria:

DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Your.class);
// then add all your restrictions and convert
Criteria criteria = detachedCriteria.getExecutableCriteria(session);

然后申请你的分页

criteria.setFirstResult(pageable.getPage() * pageable.getSize());
criteria.setMaxResults(pageable.getSize());

然后执行标准。例如:

List<T> result = criteria.list();