Hibernate - 具有分页的独特结果

时间:2012-02-23 17:43:05

标签: java sql hibernate

这似乎是一个众所周知的问题,可以在这里阅读: http://blog.xebia.com/2008/12/11/sorting-and-pagination-with-hibernate-criteria-how-it-can-go-wrong-with-joins/

甚至在hibernate faqs中找到引用:

https://community.jboss.org/wiki/HibernateFAQ-AdvancedProblems#Hibernate_does_not_return_distinct_results_for_a_query_with_outer_join_fetching_enabled_for_a_collection_even_if_I_use_the_distinct_keyword

此前也已在SO

中讨论过

How to get distinct results in hibernate with joins and row-based limiting (paging)?

问题是,即使经过所有这些资源,我也无法解决我的问题,这似乎与这个标准问题有点不同,虽然我不确定。

此处提出的标准解决方案涉及创建两个查询,第一个用于获取不同的ID,然后在更高级别的查询中使用这些查询以获得所需的分页。我的情况下的hibernate类就像

A
 - aId
 - Set<B>

B
 - bId 

在我看来,子查询似乎对我来说很好,并且能够获得不同的aIds但是应该进行分页的外部查询再次获取重复项,因此子查询中的distinct是没有效果。

假设我有一个A对象,它有一组四个B对象,我的分析是因为引入了set,​​同时为

获取数据
session.createCriteria(A.class).list();

hibernate在列表中填充四个仅指向一个对象的引用。因此,标准解决方案对我来说是失败的。

有人可以帮助为这个案例提出解决方案吗?

编辑:我决定自己从独特的结果集中进行分页。另一个同样糟糕的方法可能是延迟加载B对象,但这需要对所有A对象进行单独查询以获取相应的B对象

4 个答案:

答案 0 :(得分:6)

考虑使用像这样的DistinctRootEntity结果转换器

session.createCriteria(A.class)
    .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();

<强>更新

一对多关联的查询示例。

public Collection<Long> getIDsOfAs(int pageNumber, int pageSize) {
    Session session = getCurrentSession();

    Criteria criteria = session.createCriteria(A.class)
        .setProjection(Projections.id())
        .addOrder(Order.asc("id"));

    if(pageNumber >= 0 && pageSize > 0) {
        criteria.setMaxResults(pageSize);
        criteria.setFirstResult(pageNumber * pageSize);
    }

    @SuppressWarnings("unchecked")
    Collection<Long> ids = criteria.list();
    return ids;
}

public Collection<A> getAs(int pageNumber, int pageSize) {
    Collection<A> as = Collections.emptyList();

    Collection<Long> ids = getIDsOfAs(pageNumber, pageSize);
    if(!ids.isEmpty()) {
        Session session = getCurrentSession();

        Criteria criteria = session.createCriteria(A.class)
            .add(Restrictions.in("id", ids))
            .addOrder(Order.asc("id"))
            .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        @SuppressWarnings("unchecked")
        as = criteria.list(); 
    }    

    return as;
}

答案 1 :(得分:1)

你提到你看到这个问题的原因是因为Set<B>被急切地提取。如果你正在进行分页,那么你很可能不需要每个B使用A,所以最好是懒散地获取它们。

但是,当您将B加入查询以进行选择时,会出现同样的问题。

在某些情况下,您不仅要分页,还要对ID以外的其他字段进行排序。我认为这样做的方法是像这样制定查询:

  Criteria filter = session.createCriteria(A.class)
    .add(... any criteria you want to filter on, including aliases etc ...);
  filter.setProjection(Projections.id());

  Criteria paginate = session.createCriteria(A.class)
    .add(Subqueries.in("id", filter))
    .addOrder(Order.desc("foo"))
    .setMaxResults(max)
    .setFirstResult(first);

  return paginate.list();

(伪代码,没有检查语法是否完全正确,但你明白了)

答案 2 :(得分:0)

我在这里回答:Pagination with Hibernate Criteria and DISTINCT_ROOT_ENTITY

你需要做3件事,1)得到总数,2)得到你想要的行的ID,然后3)得到你在步骤2中找到的id的数据。真的不是那么糟糕一旦你得到正确的订单,你甚至可以创建一个通用的方法,并发送一个分离的标准对象,使其更抽象。

答案 3 :(得分:0)

我使用groupBy属性来实现这一点。希望它有效。

Criteria filter = session.createCriteria(A.class);       
filter.setProjection(Projections.groupProperty("aId"));
//filter.add(Restrictions.eq()); add restrictions if any
filter.setFirstResult(pageNum*pageSize).setMaxResults(pageSize).addOrder(Order.desc("aId"));

Criteria criteria = session.createCriteria(A.class);
criteria.add(Restrictions.in("aId",filter.list())).addOrder(Order.desc("aId"));
return criteria.list();