Hibernate Criteria API - 如何按集合大小排序?

时间:2009-08-10 11:07:02

标签: hibernate criteria-api

假设我有User和UserGroup类。有一个可选的1-many组到用户关联,并且关联是从两侧映射的(UserGroup端通过名为“members”的属性映射,这是一个HashSet,用户端通过属性“group”)。

使用Criteria API,如何查询所有组,按组成员的数量排序?

(编辑)当我问这个在SQL中执行分页时我应该指出,所以如果可能的话,也应该在SQL中执行排序。

3 个答案:

答案 0 :(得分:4)

默认情况下,使用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();

这将列出按成员升序大小排序的用户组,其中“members”是UserGroup实体中的User集合。

答案 1 :(得分:1)

另一种选择是使用@Formula注释,它基本上相当于创建一个嵌套选择,这样你就可以得到你的数量。

在您的数据库对象属性/成员(您应该创建)中,将注释添加到getter中,如:

@Formula(“(SELECT COUNT(TABLE_NAME.ID)FROM TABLE WHERE what ...)”) public long getNewProperty {    返回newProperty; }

然后直接定位会员以获取您的计数值......

注意:请注意,在公式注释中指定sql时,必须在引用当前对象表(this)时直接指定字段名称,因为hibernate会自行处理。因此,在引用当前对象表时,在WHERE子句中只使用ID或其他任何东西,我怀疑它是UserGroup。

我必须为我的新属性设置一个setter才能让hibernate工作并防止编译器抱怨。

可能还有其他聪明的方法可以使用@Formula,但我对此很新,并且似乎没有关于这个主题的很多文档......

如果您以前从未使用过嵌套的SELECTS,那么您可能只想在sql中重新创建 - 这对我有帮助。

希望这有帮助

答案 2 :(得分:0)

您可能通过在您的实体上定义“派生属性”来实现这一点,该属性是一个只读属性,它返回该实体内集合的大小。定义派生属性的最简单方法是documented here

  

更新,插入(可选 - 默认为   true):指定映射   列应包含在SQL中   UPDATE和/或INSERT语句。   将两者都设置为false允许纯粹   “衍生”属性,其值为   从其他一些财产初始化   映射到相同的列,或通过   触发器或其他应用程序。

定义属性后,您应该能够将属性名称用作条件API中的order子句,就像任何其他属性一样。不过,我自己也没试过。

如果它有效,它可能会作为内存中的排序完成,因为它无法将其映射到SQL。如果这是一个问题,那么上面的相同链接记录了如何使用自定义SQL片段实现派生属性:

  

衍生出强大的功能   属性。这些属性是由   只读定义。该物业   值在加载时计算。您   将计算声明为SQL   表达。然后转换为a   SQL中的SELECT子句子查询   加载实例的查询: