使用关联分页的Grails中的Hibernate Query的不同结果

时间:2013-06-10 08:37:41

标签: hibernate grails groovy gorm

我有三个课程相互关联。

最初我在没有分页的情况下构建查询,所有搜索结果都很好。 但现在我需要分页,不想打扰我的初始查询模式。有什么方法可以得到明显的结果。

Class Department{
    int id;
    String name;
    static hasMany = [courses:Courses]
}

Class Courses{
    String courseName;
    String courseCode;
    static hasMany = [student:Student]
    static belongsTo = [department:Department]
}

Class Student{
    String studentName;
    String address;
    static belongsTo = [courses:Courses]
}


//controller
def list = Department.createCriteria.listDistinct{
    if(params.id){
         and{eq{"id",params.id}}
    }
    and{
        courses{
            if(params.courseName){
                  and{eq("courseName",params.courseName)}
            }
        }
        and{
            student{
                 if(params.studentName){
                         and{eq("studentName",params.studentName)}
                 }
            }
        }
    }
}

我无法提供实际的表格和域名,但这种关系与上面几乎相同。它对于dintinct结果非常好,但不能分页。我尝试了一些解决方案,但它返回错误。到目前为止我还没有记录任何错误。 我已经知道listDistinct不能用于分页参数。并且列表不提供不同的参数。

我尝试了投影,但无法像之前一样检索所有属性。有什么解决方案吗?因为我需要从所有可能的属性中搜索三个表中任何一个的所有实现。我是否需要将所有查询切换到另一种方法?

3 个答案:

答案 0 :(得分:4)

前段时间我遇到类似的任务很困难 - 获得标准,分页和独特的工作,解决方案是: 1.使用list()而不是listDistinct() 2.使用maxResults和firstResult进行分页 3.使用投影{distinct'id'}来获得截然不同的结果 4.在获取id列表后,使用getAll()方法检索实际对象

所以加入它将是:

def ids = Department.createCriteria().list() {
    projections {
        distinct 'id'
    }
    maxResults params.max
    firstResult params.offset

    if(params.id){
         and{eq{"id",params.id}}
    }
    and{
        courses{
            if(params.courseName){
                  and{eq("courseName",params.courseName)}
            }
        }
        and{
            student{
                 if(params.studentName){
                         and{eq("studentName",params.studentName)}
                 }
            }
        }
    }
}
return Department.getAll(ids)

(代码现未测试)

答案 1 :(得分:0)

根据documentation

  

listDistinct ()方法不适用于分页选项maxResult和firstResult。如果您需要分页的不同结果,我们目前建议您使用HQL。

答案 2 :(得分:0)

我既不喜欢首先查询完整的ID列表,也不仅仅是一个简单的listDistinct还不够 - 因为我需要能够查询 totalCount

我的策略是

  1. 计数
  2. listDistinct
  3. 将两者都包装到PagedResultWrapper
  4. 我想出了一点 GormHelper

    package foo
    import grails.orm.HibernateCriteriaBuilder
    import groovy.util.logging.Log4j
    import org.codehaus.groovy.grails.web.util.TypeConvertingMap
    
    /**
     * Gorm Helper
     *
     * @param < T >
     */
    @Log4j
    class GormHelper<T> {
        /**
         * Clazz the helper will operate on
         */
        private final Class clazz
    
    
        private GormHelper(Class<? extends T> clazz) {
            this.clazz = clazz
        }
    
        /**
         * create a HibernateCriteriaBuilder for the
         * specified clazz
         * @return
         */
        private HibernateCriteriaBuilder createCriteria() {
            return clazz.createCriteria()
        }
    
        /**
         * List objects by conditions
         * specified in the closure
         * @param params
         * @param closure
         * @return
         */
        List<T> list(Map params, @DelegatesTo(HibernateCriteriaBuilder) Closure closure) {
            createCriteria().list(params, closure)
        }
    
        /**
         * List all objects
         * @param params
         * @return
         */
        List<T> list(Map params = [:]) {
            return list(params, {})
        }
    
        /**
         * apply a closure to a HibernateCriteriaBuilder
         * @param crit
         * @param closure
         */
        public static void apply(HibernateCriteriaBuilder crit, Closure closure) {
            if (closure == null) return
            closure.rehydrate(crit, crit, crit).call()
        }
    
        /**
         * List distinct objects
         * @param _params
         * @param closure
         * @return
         */
        List<T> listDistinct(Map _params, Closure closure) {
            listDistinct(_params, 'id', closure)
        }
    
        /**
         * List distinct objects
         * @param _params
         * @param distinctProperty
         * @param closure
         * @return
         */
        List<T> listDistinct(Map _params, String distinctProperty, Closure closure) {
            TypeConvertingMap params = new TypeConvertingMap(_params)
    
            // 1st COUNT
            Integer total = createCriteria().get {
                projections {
                    countDistinct(distinctProperty)
                }
    
                apply(delegate, closure)
            } as Integer
    
            // 2nd query distinct items with pagination data
            HibernateCriteriaBuilder crit = createCriteria()
            List<T> items = crit.listDistinct {
                apply(delegate, closure)
                setMaxResults(params.int('max'))
                setFirstResult(params.int('offset'))
            }
    
            // 3rd wrap everything into a PagedResultWrapper
            return PagedResultWrapper.wrap(total, items)
        }
    
        /**
         * Paged Result Wrapper
         *
         * holds a totalCount beside the data itself
         * can be used as a replacement of the PagedResultList
         */
        public static class PagedResultWrapper extends LinkedList {
            private final Integer total
    
            /**
             *
             * @param total
             * @param list
             */
            private PagedResultWrapper(Integer total, List list) {
                super()
                this.total = total
                addAll(list)
            }
    
            /**
             * wrap results and total into a PagedResultWrapper
             * @param total
             * @param collection
             * @return
             */
            public static PagedResultWrapper wrap(Integer total, List collection) {
                return new PagedResultWrapper(total, collection)
            }
    
            /**
             * get the total count
             * @return
             */
            public int getTotalCount() {
                return total.intValue()
            }
        }
    
        /**
         * get a Gorm Helper
         * @param clazz
         * @return
         */
        static <V> GormHelper 'for'(Class clazz, Object owner = null) {
            // TODO handle closure ownership
            return new GormHelper<V>(clazz)
        }
    
    
    }
    

    你会像这样使用它:

    def usersInSelectedGroups = GormHelper.for(User).listDistinct([max: 10, offset: 20]){
      groups {
        inList('id', [1001, 1002, 1003])
      }
    }
    
    println "got ${usersInSelectedGroups.totalCount} users"
    usersInSelectedGroups.each {println "\t ${it.username}" }
    

    这将输出具有ID 1001,1002,1003 的组中的所有用户 - 如果用户是其中几个组的成员,它们将仅在结果列表中列出一次< / strong> ...