【问题标题】:Hibernate Criteria API - how to order by collection size?Hibernate Criteria API - 如何按集合大小排序?
【发布时间】:2010-11-18 06:22:48
【问题描述】:

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

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

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

【问题讨论】:

    标签: hibernate criteria-api


    【解决方案1】:

    默认情况下无法使用 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 实体中的用户集合。

    【讨论】:

      【解决方案2】:

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

      在您的 DB 对象属性/有问题的成员(您应该创建)上,将注释添加到 getter,如下所示:

      @Formula("(SELECT COUNT(TABLE_NAME.ID) FROM TABLE WHERE...)") 公共长 getNewProperty{ 返回新属性; }

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

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

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

      可能还有其他巧妙的方法可以使用@Formula,但我对此很陌生,而且似乎没有太多关于该主题的文档...

      如果您以前从未使用过嵌套 SELECTS,那么您最好先在 sql 中重新创建 - 这对我有帮助。

      希望对你有帮助

      【讨论】:

      • 虽然这个实现看起来有点可疑(就休眠文档而言),但它确实有效!
      【解决方案3】:

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

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

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

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

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

      【讨论】:

        猜你喜欢
        • 2012-11-12
        • 1970-01-01
        • 1970-01-01
        • 2011-08-31
        • 1970-01-01
        • 2020-09-02
        • 2012-01-06
        • 1970-01-01
        • 2015-02-26
        相关资源
        最近更新 更多