这似乎是一个众所周知的问题,可以在这里阅读: http://blog.xebia.com/2008/12/11/sorting-and-pagination-with-hibernate-criteria-how-it-can-go-wrong-with-joins/
甚至在hibernate faqs中找到引用:
此前也已在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对象
答案 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();