技术开发 频道

精通 Grails: Grails 事件模型

  微型事件

  了解一些大型事件后,现在看几个微型事件。

  为域类添加时间戳

  如果您提供几个特别的命名字段,GORM 会自动给它们添加时间戳,如清单 15 所示:

  清单 15. 为字段添加时间戳

class Hotel{
  
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 中删除时间戳字段

excludedProps = ['dateCreated','lastUpdated',
                 'version',
                 'id',
                   Events.ONLOAD_EVENT,
                   Events.BEFORE_DELETE_EVENT,
                   Events.BEFORE_INSERT_EVENT,
                   Events.BEFORE_UPDATE_EVENT]

  这会限制在创建和编辑视图中创建字段,但仍然在列表中保留字段并显示视图。创建一两个 Hotel 并验证字段会自动更新。

  如果应用程序已经使用这些字段名称,可以轻松地禁用此功能,如清单 17 所示:

  清单 17. 禁用时间戳

static mapping = {
  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. 映射时间戳字段

class Hotel{
  
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 闭包

class Hotel{
  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 模板中。这样,新创建域类时它们就有适当的字段和事件闭包。

0
相关文章