5

I have three class interlinked with each other.

Initially I build query without pagination and all search results were fine. But now I need pagination and don't want to disturb my initial query pattern. Is there any way I could get distinct results.

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)}
                 }
            }
        }
    }
}

I could not gave you the actual tables and domains, but the relation is pretty much the same as above. It worked really fine for dintinct results but couldnot paginate. I have tried a number of solution but it returns error. I have not recorded any errors till now. I have came to know listDistinct can not be used for pagination parameters. and list doesnot provide distinct parameters.

I tried projection but couldnot retrieve all the attributes as before. Is there any solutions to it. As I need to search from all possible attributes with all the realtion from any of the three tables. Do I need to switch all my query to another method?

4

4 回答 4

7

前段时间我在完成类似任务时遇到了困难 - 让标准、分页和不同的东西一起工作,解决方案是: 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)

(代码现在没有测试)

于 2013-06-10T23:03:26.220 回答
1

根据文档

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

于 2013-06-10T08:44:11.157 回答
0

我既不喜欢首先进行查询来检索完整的 id 列表,也不是仅仅一个简单的 listDistinct 还不够——因为我需要能够查询totalCount

我的策略是

  1. 数数
  2. listDistinct
  3. 将两者包装到 PagedResultWrapper

我想出了一个小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 的组中的所有用户- 如果用户是其中几个组的成员,他们将只在结果列表中列出一次......

于 2015-09-15T18:42:52.663 回答
0

尝试这个:

def search = Department.createCriteria.list(max: max, offset: offset){
//Enter conditions here
}

接着:

search.unique().totalCount for accessing the total number of results
search.unique() to return back the results.

希望这可以帮助!

干杯

于 2015-08-25T12:07:37.120 回答