假设我有类用户和用户组。有一个可选的 1-many group 到用户关联,并且关联从双方映射(UserGroup 侧通过名为“members”的属性,这是一个 HashSet,User 侧通过属性“group”)。
使用 Criteria API,我如何查询所有组,按组成员数排序?
(编辑)当我问这个问题时,我应该指出分页是在 SQL 中执行的,所以如果可能的话,排序也应该在 SQL 中执行。
假设我有类用户和用户组。有一个可选的 1-many group 到用户关联,并且关联从双方映射(UserGroup 侧通过名为“members”的属性,这是一个 HashSet,User 侧通过属性“group”)。
使用 Criteria API,我如何查询所有组,按组成员数排序?
(编辑)当我问这个问题时,我应该指出分页是在 SQL 中执行的,所以如果可能的话,排序也应该在 SQL 中执行。
默认情况下无法使用 Citeria API,但您可以扩展 org.hibernate.criterion.Order 类。本文是关于如何扩展该类:http ://blog.tremend.ro/2008/06/10/how-to-order-by-a-custom-sql-formulaexpression-when-using-hibernate-criteria- api
我的解决方案是:
public class SizeOrder extends Order {
protected String propertyName;
protected boolean ascending;
protected SizeOrder(String propertyName, boolean ascending) {
super(propertyName, ascending);
this.propertyName = propertyName;
this.ascending = ascending;
}
public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
String role = criteriaQuery.getEntityName(criteria, propertyName) + '.' + criteriaQuery.getPropertyName(propertyName);
QueryableCollection cp = (QueryableCollection) criteriaQuery.getFactory().getCollectionPersister(role);
String[] fk = cp.getKeyColumnNames();
String[] pk = ((Loadable) cp.getOwnerEntityPersister())
.getIdentifierColumnNames();
return " (select count(*) from " + cp.getTableName() + " where "
+ new ConditionFragment()
.setTableAlias(
criteriaQuery.getSQLAlias(criteria, propertyName)
).setCondition(pk, fk)
.toFragmentString() + ") "
+ (ascending ? "asc" : "desc");
}
public static SizeOrder asc(String propertyName) {
return new SizeOrder(propertyName, true);
}
public static SizeOrder desc(String propertyName) {
return new SizeOrder(propertyName, false);
}
}
toSqlString 方法基于 org.hibernate.criterion.SizeExpression 类。(来源:http: //javasourcecode.org/html/open-source/hibernate/hibernate-3.6.0.Final/org/hibernate/criterion/SizeExpression.java.html)
示例用法:
hibernateSession.createCriteria(UserGroup.class).addOrder( SizeOrder.asc("members") ).list();
这将列出按成员大小升序排列的用户组,其中“成员”是 UserGroup 实体中的用户集合。
另一种选择是使用 @Formula 注释,它基本上相当于创建一个嵌套选择,这样你就可以计算出你的数量。
在您的数据库对象属性/有问题的成员(您应该创建)上,将注释添加到 getter,如:
@Formula("(SELECT COUNT(TABLE_NAME.ID) FROM TABLE WHERE...)") public long getNewProperty{ return newProperty; }
然后直接定位该成员以获取您的计数值...
注意:请注意,在公式注释中指定 sql 时,必须在引用当前对象表 (this) 时直接指定字段名称,因为 hibernate 自己处理 this。因此,在您的 WHERE 子句中,当引用我怀疑是 UserGroup 的当前对象表时,只需使用 ID 或其他任何内容。
我必须为我的新属性设置一个 setter 才能让 hibernate 工作并防止编译器抱怨。
使用@Formula 可能还有其他巧妙的方法,但我对此很陌生,似乎没有太多关于该主题的文档......
如果您以前从未使用过嵌套的 SELECTS,那么您最好先在 sql 中重新创建 - 这对我有帮助。
希望这可以帮助
您可以通过在您的实体上定义一个“派生属性”来实现这一点,这将是一个只读属性,它返回该实体内集合的大小。此处记录了定义派生属性的最简单方法:
update, insert(可选 - 默认为 true):指定映射列应包含在 SQL UPDATE 和/或 INSERT 语句中。将两者都设置为 false 允许纯“派生”属性,其值是从映射到同一列的其他属性或由触发器或其他应用程序初始化的。
定义属性后,您应该能够将属性名称用作条件 API 中的 order 子句,就像任何其他属性一样。不过,我自己还没有尝试过。
如果它有效,它可能会作为内存排序完成,因为它无法将其映射到 SQL。如果这是一个问题,那么上面的相同链接记录了如何使用自定义 SQL 片段实现派生属性:
一个强大的功能是派生属性。根据定义,这些属性是只读的。属性值在加载时计算。您将计算声明为 SQL 表达式。然后,这将转换为加载实例的 SQL 查询中的 SELECT 子句子查询: