如何通过连接和基于行的限制(分页)在休眠中获得不同的结果?

问题描述 投票:0回答:10

我正在尝试在连接到其他表的 Hibernate Criteria 查询上使用基于行的限制(例如:

setFirstResult(5)
setMaxResults(10)
)来实现分页。

可以理解,数据被随机切断;其原因已解释here

作为解决方案,该页面建议使用“第二个 sql select”而不是联接。

如何将现有条件查询(使用

createAlias()
进行联接)转换为使用嵌套选择?

java hibernate pagination distinct criteria
10个回答
107
投票

您可以通过请求不同 id 的列表而不是不同水合对象的列表来实现所需的结果。

只需将此添加到您的标准中即可:

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

现在您将根据基于行的限制获得正确数量的结果。这样做的原因是因为投影将执行唯一性检查作为 sql 查询的一部分,而不是 ResultTransformer 执行的操作是在执行 sql 查询之后过滤结果的唯一性。 值得注意的是,您现在将获得一个 id 列表,而不是获取对象列表,稍后您可以使用它来从休眠中水合对象。


43
投票

只需将此添加到您的标准中即可:

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

该代码将类似于本机 sql 的表中的 select different * 。


29
投票

可以一次完成这种查询,而不是分两个单独的阶段。即下面的单个查询将正确分页不同的结果,并且还返回实体而不仅仅是 ID。

只需使用带有 id 投影的 DetachedCriteria 作为子查询,然后在主 Criteria 对象上添加分页值。

它看起来像这样:

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();



6
投票

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



5
投票

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

效果很好。


4
投票
这对我有帮助:D


3
投票

criteria.setProjection( Projections.distinct( Projections.projectionList() .add(Projections.id()) .add(Projections.property("the property that you want to ordered by")) ) );



1
投票

该解决方案的优点是:

比本文提到的PK id解决方案更快
  • 保留排序并且不要在可能很大的 PK 数据集上使用“in 子句”
  • 完整文章可以在
我的博客

找到 Hibernate 不仅可以在设计时定义关联获取方法,还可以在运行时通过查询执行来定义关联获取方法。因此,我们将这种方法与简单的反射材料结合使用,并且还可以自动更改仅针对集合属性的查询属性获取算法的过程。

首先我们创建一个方法来解析实体类中的所有集合属性:

public static List<String> resolveCollectionProperties(Class<?> type) { List<String> ret = new ArrayList<String>(); try { BeanInfo beanInfo = Introspector.getBeanInfo(type); for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) { if (Collection.class.isAssignableFrom(pd.getPropertyType())) ret.add(pd.getName()); } } catch (IntrospectionException e) { e.printStackTrace(); } return ret; }

完成此操作后,您可以使用这个小帮助器方法,建议您的条件对象将该查询的 FetchMode 更改为 SELECT。

Criteria criteria = … // … add your expression here … // set fetchmode for every Collection Property to SELECT for (String property : ReflectUtil.resolveCollectionProperties(YourEntity.class)) { criteria.setFetchMode(property, org.hibernate.FetchMode.SELECT); } criteria.setFirstResult(firstResult); criteria.setMaxResults(maxResults); criteria.list();

这样做与在设计时定义实体的 FetchMode 不同。因此,您可以在 UI 中对分页算法使用正常的连接关联获取,因为这在大多数情况下不是关键部分,更重要的是尽快获得结果。


0
投票

package org.hibernate.criterion; import org.hibernate.Criteria; import org.hibernate.Hibernate; import org.hibernate.HibernateException; import org.hibernate.type.Type; /** * A count for style : count (distinct (a || b || c)) */ public class MultipleCountProjection extends AggregateProjection { private boolean distinct; protected MultipleCountProjection(String prop) { super("count", prop); } public String toString() { if(distinct) { return "distinct " + super.toString(); } else { return super.toString(); } } public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException { return new Type[] { Hibernate.INTEGER }; } public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery) throws HibernateException { StringBuffer buf = new StringBuffer(); buf.append("count("); if (distinct) buf.append("distinct "); String[] properties = propertyName.split(";"); for (int i = 0; i < properties.length; i++) { buf.append( criteriaQuery.getColumn(criteria, properties[i]) ); if(i != properties.length - 1) buf.append(" || "); } buf.append(") as y"); buf.append(position); buf.append('_'); return buf.toString(); } public MultipleCountProjection setDistinct() { distinct = true; return this; } }

ExtraProjections.java

package org.hibernate.criterion; public final class ExtraProjections { public static MultipleCountProjection countMultipleDistinct(String propertyNames) { return new MultipleCountProjection(propertyNames).setDistinct(); } }

用法示例:

String propertyNames = "titleName;titleDescr;titleVersion" criteria countCriteria = .... countCriteria.setProjection(ExtraProjections.countMultipleDistinct(propertyNames);

参考自
https://forum.hibernate.org/viewtopic.php?t=964506


-1
投票
NullPointerException

在某些情况下! 没有

criteria.setProjection(Projections.distinct(Projections.property("id")))
所有查询都很顺利! 这个解决方案很糟糕!
另一种方法是使用 SQLQuery。就我而言,以下代码工作正常:

List result = getSession().createSQLQuery( "SELECT distinct u.id as usrId, b.currentBillingAccountType as oldUser_type," + " r.accountTypeWhenRegister as newUser_type, count(r.accountTypeWhenRegister) as numOfRegUsers" + " FROM recommendations r, users u, billing_accounts b WHERE " + " r.user_fk = u.id and" + " b.user_fk = u.id and" + " r.activated = true and" + " r.audit_CD > :monthAgo and" + " r.bonusExceeded is null and" + " group by u.id, r.accountTypeWhenRegister") .addScalar("usrId", Hibernate.LONG) .addScalar("oldUser_type", Hibernate.INTEGER) .addScalar("newUser_type", Hibernate.INTEGER) .addScalar("numOfRegUsers", Hibernate.BIG_INTEGER) .setParameter("monthAgo", monthAgo) .setMaxResults(20) .list();

区别是在数据库中完成的!

与:相反 criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

在加载实体之后,在内存中进行区分!

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.