Grails框架如何动态实现createCriteria的where过滤与计数查询
问题根因
GORM的where方法基于DetachedCriteria DSL实现,闭包内的标识符会被直接解析为绑定Domain类(也就是Release)的持久化属性,不会读取外部作用域的colName变量值——代码会尝试匹配Release中名为colName的字段,和你预期的动态列查询逻辑完全不符,自然会出现计数异常。
另外原代码存在两个隐性bug:
- 带搜索条件的criteria查询没有传入
max/offset分页参数,分页逻辑实际未生效 - 列表查询和计数查询逻辑分离,后续调整查询条件时很容易出现两边逻辑不同步,导致计数和实际返回列表条数不匹配
推荐实现方案
不需要单独写计数逻辑,直接复用同一个criteria查询即可:调用criteria的list方法时传入分页参数,返回的结果集会自动携带totalCount属性,值就是符合条件的结果总数,既天然支持动态传列名,也不会出现查询条件不一致的问题。
修正后的完整代码:
def search(Integer max, Integer offset) { def searchText = params.searchText def colName = params.colName params.max = params.int('max') ?: 10 params.offset = params.int('offset') ?: 0 def releaseList def releaseCount if (searchText && colName) { // 必须校验列名合法性,避免SQL注入风险 def validColumns = Release.gormPersistentEntity.persistentPropertyNames if (colName !in validColumns) { throw new IllegalArgumentException("非法查询字段: ${colName}") } def criteria = Release.createCriteria() releaseList = criteria.list(max: params.max, offset: params.offset) { eq(colName, searchText) } releaseCount = releaseList.totalCount } else { releaseList = Release.list(params) releaseCount = Release.count() } render(template: 'grid', model: [ ReleaseInstanceList: releaseList, ReleaseInstanceCount: releaseCount, searchText: searchText ]) }
单独使用where块的实现方式
如果有特殊场景必须拆分列表查询和计数查询,不要在where闭包内直接引用动态列名,通过DetachedCriteria提供的eq方法动态传入属性名即可:
def detachedQuery = Release.where {} detachedQuery.eq(colName, searchText) releaseCount = detachedQuery.count()
注意:所有接收前端传入列名做动态查询的场景,都必须提前校验列名是否属于当前Domain的合法持久化字段,直接使用前端传入的参数拼接查询会存在SQL注入风险。
内容的提问来源于stack exchange,提问作者Suprina Paudel
相关产品推荐
相关产品推荐

