领先一步
VMware 提供培训和认证,助力您快速进步。
了解更多上周,我描述了 Grails 如何将插件视为普通的依赖项,可以从 Maven 兼容的仓库中拉取。尽管这是 1.3 版本的主要新特性,但并非唯一。在本文中,我将介绍其他一些特性,首先是我最近才发现的一个特性。
开发一个非平凡的 Grails 应用,您很快就会意识到经常需要反复使用相同的查询。您该怎么办?复制粘贴技术很简单,但会导致主要的维护问题。您可以为每个通用查询编写服务方法,但这会导致服务粒度过细,并且领域模型变得相当愚蠢。这些查询的理想位置是在领域类本身上。
这就是命名查询的作用所在——一个在 Grails 1.2 中悄然出现的特性。
基本思想是,一份报告可以关于一台或多台服务器,并且每月可能生成一次或多次。所以dow代表“星期几”(day of week),而wom代表“月度周次”(week of month)。每台服务器都有一个关联的位置,在这个大大简化的模型中,它仅仅是一个城市名称。
现在考虑一下应用程序可能想从这个模型中提取什么样的信息:也许是所有在月度第一周生成的报告,或者所有关于特定服务器的报告。我们可以向Report类添加静态方法来提供此类查询,但命名查询为我们提供了一些额外的优势,稍后您将看到。
正如您对 Grails 的预期一样,创建命名查询非常简单。只需向相关的领域类添加一个静态的namedQueries属性,并为其分配一个闭包
class Report {
String name
static hasMany = [frequencies: Frequency, servers: Server]
static namedQueries = {
inFirstWeek {
frequencies {
eq("wom", 1)
}
}
inWeek { wom ->
frequencies {
eq("wom", wom)
}
}
dilbertsReports {
servers {
eq("mgrEmail", "[email protected]")
}
}
inCity { city ->
servers {
location {
eq("city", city)
}
}
}
}
上面的代码设置了四个查询:inFirstWeek、inWeek、dilbertsReports 和 inCity。然后,您可以在可以使用动态查找器的地方使用它们,例如在控制器动作或服务方法中。如果您想检索所有在月度第一周生成的报告,请像这样调用相关的命名查询
Report.inFirstWeek.list()
如果您想获取所有在月度其他周生成的报告,请使用inWeek代替
Report.inWeek(2).list()
看到如何向命名查询传递参数了吗?只需确保您的命名查询闭包声明了适当数量的参数。
希望您能看到声明和使用命名查询是多么容易,但在继续之前,有几点需要澄清。
首先,您必须使用 Grails 的 criteria DSL 编写查询。如果您一直推迟学习 criteria DSL,那么现在您有了充分的理由停止拖延!
其次,您将 DSL 调用为静态属性(如果您不向其传递任何参数)或方法,然后是标准的 GORM 检索方法,例如list(), get()或动态查找器。这意味着您可以向命名查询添加额外的过滤。还需要指出的是,get()只有在命名查询结果包含所需实体的情况下,才会返回领域实例。否则,get()将简单地返回null.
换句话说,假设inFirstWeek查询返回 ID 为 1、3 和 6 的领域实例。那么
Report.inFirstWeek.get(3)
将返回 ID 为 3 的领域实例,而
Report.inFirstWeek.get(2)
将返回null,即使Report.get(2)返回一个真实的领域实例。因此,命名查询就像一个过滤器。
到目前为止,一切顺利。命名查询与get(), list()和动态查找器的结合方式可能已经足以成为您立即使用它们的理由。但 Grails 1.3 还藏着另一个绝招。
Report.dilbertsReports.inFirstWeek.list()
或者,如果我想获取所有关于伦敦服务器的第一周报告,我可以使用
Report.inFirstWeek.inCity("London").list()
实际上,您可以链式调用任意数量的命名查询,只要它们都返回相同类型的领域类。
命名查询提供了一种强大的查询重用技术,实现和使用都非常简单。现在您可以拥有一个非常丰富的领域模型,并且客户端代码易于阅读和理解。这有多棒?
现在我想快速看一下其他 Grails 1.3 特性。
def book = Book.get(10)
assert !book.dirty
book.title = "Unknown"
assert book.dirty
assert book.isDirty("title")
assert !book.isDirty("author")
看到如何检查单个字段是否已修改了吗?
grails.sitemesh.default.layout = 'defaultLayout'
或者创建文件grails-app/views/layouts/application.gsp。第一种方法会从grails-app/views/layouts/defaultLayout.gsp.
对于所有测试爱好者来说,Grails 终于默认捆绑了 JUnit 4,所以您现在可以随心所欲地注解您的测试用例了。至此,我将结束关于 Grails 1.3 特性的这一部分。希望您能好好利用命名查询!下次,我将介绍 in-place 插件。