精通 Grails: Grails 事件模型
微型事件
了解一些大型事件后,现在看几个微型事件。
为域类添加时间戳
如果您提供几个特别的命名字段,GORM 会自动给它们添加时间戳,如清单 15 所示:
清单 15. 为字段添加时间戳
String name
Date dateCreated
Date lastUpdated
}
顾名思义,dateCreated 字段在数据第一次插入到数据库时被填充。lastUpdated 字段在每次数据库记录更新之后被填充。
要验证这些字段在幕后被填充,需要再做一件事:在创建和编辑视图中禁用它们。为此,可以输入 grails generate-views Hotel 并删除 create.gsp 和 edit.gsp 文件中的字段,但有一种方法使 scaffolded 视图更具动态性。在 “用 Groovy 服务器页面(GSP)改变视图” 中,您输入了 grails install-templates,以便能够调试 scaffolded 视图。查看 scripts/templates/scaffolding 中的 create.gsp 和 edit.gsp。现在向模板中的 excludedProps 列表添加两个时间戳字段,如清单 16 所示:
清单 16. 从默认 scaffolding 中删除时间戳字段
'version',
'id',
Events.ONLOAD_EVENT,
Events.BEFORE_DELETE_EVENT,
Events.BEFORE_INSERT_EVENT,
Events.BEFORE_UPDATE_EVENT]
这会限制在创建和编辑视图中创建字段,但仍然在列表中保留字段并显示视图。创建一两个 Hotel 并验证字段会自动更新。
如果应用程序已经使用这些字段名称,可以轻松地禁用此功能,如清单 17 所示:
清单 17. 禁用时间戳
autoTimestamp false
}
回忆一下 “Grails 与遗留数据库”,在那里还可以指定 version false 来禁用 version 字段的自动创建和更新。
向域类添加事件处理程序
除了给域类添加时间戳,还可以引入 4 个事件挂钩:beforeInsert、befortUpdate、beforeDelete 和 onload。
这些闭包名称反映了它们的含义。beforeInsert 闭包在 save() 方法之前调用。beforeUpdate 闭包在 update() 方法之前调用。beforeDelete 闭包在 delete() 方法之前调用。最后,从数据库加载类后调用 onload。
假设您的公司已经制有给数据库记录加时间戳的策略,而且将这些字段的名称标准化为 cr_time 和 up_time。有几个方案可使 Grails 符合这个企业策略。一个是使用在 “Grails 与遗留数据库” 中学到的静态映射技巧将默认 Grails 字段名称与默认公司列名称关联,如清单 18 所示:
清单 18. 映射时间戳字段
Date dateCreated
Date lastUpdated
static mapping = {
columns {
dateCreated column: "cr_time"
lastUpdated column: "up_time"
}
}
}
另一种方案是将域类中的字段命名为与企业列名称匹配的名称,并创建 beforeInsert 和 beforeUpdate 闭包来填充字段,如清单 19 所示(不要忘记将新字段设置为 nullable — 否则 save() 方法会在 BootStrap.groovy 中静默失败)。
清单 19. 添加 beforeInsert 和 beforeUpdate 闭包
static constraints = {
name()
crTime(nullable:true)
upTime(nullable:true)
}
String name
Date crTime
Date upTime
def beforeInsert = {
crTime = new Date()
}
def beforeUpdate = {
upTime = new Date()
}
}
启动和停止应用程序几次,确保新字段按预期填充。
像到目前为止看到的所有其他事件一样,您可以决定如何使用它们。回忆一下 “Grails 服务和 Google 地图”,您创建了一个 Geocoding 服务来将街道地址转换为纬度/经度坐标,以便可以在地图上标示一个 Airport。在那篇文章中,我让您在 AirportController 中调用 save 和 update 闭包中的服务。我曾试图将此服务调用移动到 Airport 类中的 beforeInsert 和 beforeUpdate,以使它能够透明地自动发生。
如何在所有类中共享这个行为呢?我将这些字段和闭包添加到 src/templates 中的默认 DomainClass 模板中。这样,新创建域类时它们就有适当的字段和事件闭包。