技术开发 频道

Java开发2.0:NoSQL

  Groovy 技巧

  如前所述,Groovy 的动态特性允许我通过 get和 set Property方法捕获对不存在的属性的方法调用。这样,清单 2 中的 Model的子类不必定义它们自己的属性 —它们只是将对一个属性的所有调用委托给这个底层 entity对象。

  清单 2 中的代码执行了一些特定于 Groovy 的操作,值得一提。首先,可以通过在一个属性前面附加一个 @来绕过该属性的访问器方法。我必须对构造函数中的 entity对象引用执行上述操作,否则我将调用 setProperty方法。很明显,在这个关头调用 setProperty将打破这种模式,因为 setProperty方法中的 entity变量将是 null。

  其次,构造函数中的调用 this.getClass().simpleName将设置 entity的 “种类” — simpleName属性将生成一个不带包前缀的子类名称(注意,simpleName的确是对 getSimpleName的调用,但 Groovy 允许我不通过对应的 JavaBeans 式的方法调用来尝试访问一个属性)。

  最后,如果对 id属性(即,对象的键)进行一个调用,getProperty方法很智能,能够询问底层 key以获取它的 id。在 Google App Engine 中,entities的 key属性将自动生成。

  Race 子类

  定义 Race子类很简单,如清单 3 所示:

  清单 3. 一个 Race 子类

package com.b50.nosql

class Race extends Model {
public Race(params){
  super(params)
}
}

  当一个子类使用一列参数(即一个包含多个 “键 / 值” 对的 Map)实例化时,一个对应的 entity将在内存中创建。要持久存储它,只需调用 save方法。

  清单 4. 创建一个 Race 实例并将其保存到 GAE 的数据存储 

import com.b50.nosql.Runner

  def iparams
= [:]

  def formatter
= new SimpleDateFormat("MM/dd/yyyy")

  def rdate
= formatter.parse("04/17/2010")

  iparams[
"name"] = "Charlottesville Marathon"

  iparams[
"date"] = rdate

  iparams[
"distance"] = 26.2 as double

  def race
= new Race(iparams)

  race.save()

  清单 4 是一个 Groovlet,其中,一个 Map(称为 iparams)创建为带有 3 个属性 —一次比赛的名称、日期和距离。(注意,在 Groovy 中,一个空白 Map通过 [:]创建。)Race的一个新实例被创建,然后通过 save方法存储到底层数据存储。

  可以通过 Google App Engine 控制台来查看底层数据存储,确保我的数据的确在那里,如图 2 所示:

  图 2. 查看新创建的 Race

  查找程序方法生成持久存储的实体

  现在我已经存储了一个 Entity,拥有查找它的能力将有所帮助。接下来,我可以添加一个 “查找程序” 方法。在本例中,我将把这个 “查找程序” 方法创建为一个类方法(static)并且允许通过名称查找这些 Race(即基于 name属性搜索)。稍后,总是可以通过其他属性添加其他查找程序。

  我还打算对我的查找程序采用一个惯例,即指定:任何名称中不带单词 all的查找程序都企图找到 一个实例。名称中包含单词 all的查找程序(如 findAllByName)能够返回一个实例 Collection或 List。清单 5 展示了 findByName查找程序:

  清单 5. 一个基于 Entity 名称搜索的简单查找程序

static def findByName(name){

  def query
= new Query(Race.class.simpleName)

  query.addFilter(
"name", Query.FilterOperator.EQUAL, name)

  def preparedQuery
= this.datastore.prepare(query)

  
if(preparedQuery.countEntities() > 1){

  return
new Race(preparedQuery.asList(withLimit(1))[0])

  }
else{

  return
new Race(preparedQuery.asSingleEntity())

  }

  }

  这个简单的查找程序使用 Google App Engine 的 Query和 PreparedQuery类型来查找一个类型为 “Race” 的实体,其名称(完全)等同于传入的名称。如果有超过一个 Race符合这个标准,查找程序将返回一个列表的第一项,这是分页限制 1(withLimit(1))所指定的。

  对应的 findAllByName与上述方法类似,但添加了一个参数,指定 您想要的实体个数,如清单 6 所示:

  清单 6. 通过名称找到全部实体

static def findAllByName(name, pagination=10){

  def query
= new Query(Race.class.getSimpleName())

  query.addFilter(
"name", Query.FilterOperator.EQUAL, name)

  def preparedQuery
= this.datastore.prepare(query)

  def entities
= preparedQuery.asList(withLimit(pagination as int))

  return entities.collect {
new Race(it as Entity) }

  }

  与前面定义的查找程序类似,findAllByName通过名称找到 Race实例,但是它返回 所有 Race。顺便说一下,Groovy 的 collect方法非常灵活:它允许删除创建 Race实例的对应的循环。注意,Groovy 还支持方法参数的默认值;这样,如果我没有传入第 2 个值,pagination将拥有值 10。

  清单 7. 查找程序的实际运行

def nrace = Race.findByName("Charlottesville Marathon")

  assert nrace.distance
== 26.2

  def races
= Race.findAllByName("Charlottesville Marathon")

  assert races.class
== ArrayList.class

  清单 7中的查找程序按照既定的方式运行:findByName返回一个实例,而 findAllByName返回一个 Collection(假定有多个 “Charlottesville Marathon”)。

0
相关文章