Grails如何在非数据库映射类中实现class.list(params)功能?
解决Grails中非数据库映射类的分页排序问题
这个问题我之前也碰到过,核心原因是你搞混了GORM域类和自定义视图模型类的区别——list(params)是Grails为映射到数据库的GORM域类提供的内置方法,你的Actor是自定义的视图模型(没有和数据库表绑定),自然没有这个方法,所以调用会报错。下面给你两种可行的解决方案,根据你的数据量来选:
方案一:数据库层面处理分页排序(推荐)
因为你本来就是用SQL查询获取数据,直接把分页、排序逻辑写到SQL里是效率最高的方式,尤其适合数据量大的场景。SQL Server 2012及以上支持OFFSET/FETCH语法来实现分页,排序则通过ORDER BY结合参数处理(要注意防SQL注入)。
示例代码如下:
def index(Integer max) { // 初始化分页参数 params.max = Math.min(max ?: 10, 100) params.offset = params.offset ?: 0 // 定义允许排序的字段,防止SQL注入 def allowedSortFields = ['name', 'age', 'createDate'] // 替换成你实际的字段 def sortField = allowedSortFields.contains(params.sort) ? params.sort : 'name' def sortOrder = params.order in ['asc', 'desc'] ? params.order : 'asc' def sql = new Sql(dataSource) try { // 查询分页后的数据 def actorRows = sql.rows(""" SELECT name, age, createDate FROM your_actual_table_name ORDER BY ${sortField} ${sortOrder} OFFSET ${params.offset} ROWS FETCH NEXT ${params.max} ROWS ONLY """) // 转换为你的Actor视图模型实例 def ActorInstanceList = actorRows.collect { row -> new Actor( name: row.name, age: row.age, createDate: row.createDate // 其他字段按需添加 ) } // 查询总记录数,用于前端分页组件 def totalCount = sql.firstRow("SELECT COUNT(*) AS total FROM your_actual_table_name").total as Integer params.total = totalCount model:[ActorInstanceList: ActorInstanceList, params: params] } finally { sql.close() // 记得关闭Sql连接 } }
方案二:内存中处理分页排序(适合小数据量)
如果你的数据量很小(比如几百条以内),可以先把所有数据查询出来,再在内存中处理排序和分页。这种方式不用修改SQL,但数据量大时性能会很差。
示例代码:
def index(Integer max) { // 初始化分页参数 params.max = Math.min(max ?: 10, 100) params.offset = params.offset ?: 0 def sql = new Sql(dataSource) try { // 查询所有数据并转换为Actor实例 def allActors = sql.rows("SELECT name, age, createDate FROM your_actual_table_name").collect { row -> new Actor( name: row.name, age: row.age, createDate: row.createDate ) } // 处理排序 def allowedSortFields = ['name', 'age', 'createDate'] def sortField = allowedSortFields.contains(params.sort) ? params.sort : 'name' def sortOrder = params.order in ['asc', 'desc'] ? params.order : 'asc' def sortedActors = allActors.sort { a, b -> sortOrder == 'asc' ? a."${sortField}" <=> b."${sortField}" : b."${sortField}" <=> a."${sortField}" } // 处理分页 def startIndex = params.offset as Integer def endIndex = Math.min(startIndex + params.max as Integer, sortedActors.size()) def ActorInstanceList = sortedActors.subList(startIndex, endIndex) // 总记录数 params.total = allActors.size() model:[ActorInstanceList: ActorInstanceList, params: params] } finally { sql.close() } }
关键注意点
- 防SQL注入:绝对不能直接把用户传入的
params.sort拼到SQL里,一定要校验是否在允许的字段列表中,避免恶意SQL注入。 - 资源释放:使用
Sql类时,记得在finally块中关闭连接,防止资源泄漏。 - 前端分页支持:把
params.total传到模型中,Grails的<g:paginate>标签就能自动识别并生成正确的分页导航。
内容的提问来源于stack exchange,提问作者Bmoe
相关产品推荐
相关产品推荐

