更多 Grails 1.3 功能

工程 | Peter Ledbrook | 2010 年 5 月 24 日 | ...

上周,我描述了 Grails 如何将插件视为可以从 Maven 兼容存储库中拉取的普通依赖项。虽然这是 1.3 的一个重要新功能,但并非唯一的功能。在这篇文章中,我将介绍一些其他功能,并从我最近才发现的一个功能开始。

命名查询

GORM 提供了三种不同的数据库查询方式
  • 动态查找器,例如 Book.findByTitleAndAuthorLike(...);
  • Criteria 查询,使用漂亮的 DSL;以及
  • HQL,Hibernate 的类似 SQL 的查询语言。
这三个功能提供了易用性和强大功能的强大组合,为您提供了所需的灵活性。然而,仍然缺少一些东西。

开发一个非琐碎的 Grails 应用程序,您很快就会意识到您经常重复使用相同的查询。您应该怎么做?复制粘贴技术很简单,但会留下重大的维护问题。您可以为每个常用查询编写服务方法,但随后会得到相当精细的服务和相当“愚钝”的域模型。这些查询的理想位置是域类本身。

这就是命名查询的用武之地——这项功能在 Grails 1.2 中悄然出现。

一个例子

让我们考虑一个与报告相关的简单域模型:[caption id="attachment_4791" align="alignnone" width="398"]Domain model for reports[/caption]

基本思想是,一份报告可以涉及一个或多个服务器,并且可能每月生成一次或多次。所以dow代表“一周中的某一天”,而wom代表“一个月中的第几周”。每台服务器都有一个关联的位置,在这个大大简化的模型中,这只是一个城市名称。

现在考虑一下应用程序可能想从这个模型中提取哪些信息:也许是本月第一周生成的所有报告,或者是关于特定服务器的所有报告。我们可以将静态方法添加到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,现在您有一个很好的理由停止拖延!

其次,您可以像调用标准 GORM 检索方法(例如list(), get()find()` 或 `findAll()`)或动态查找器一样,通过静态属性(如果您不向其传递任何参数)或方法来调用 DSL。这意味着您可以为命名查询添加额外的过滤。还值得指出的是,get()`Report.get(reportId)` 将仅在命名查询的结果包含所需实体时才返回域实例。否则,get()`Report.get(reportId)` 将简单地返回null.

null。换句话说,假设inFirstWeek查询返回 ID 为 1、3 和 6 的域实例。那么

Report.inFirstWeek.get(3)

`Report.get(3)` 将返回 ID 为 3 的域实例,而

Report.inFirstWeek.get(2)

`Report.get(2)` 将返回nullnull,即使Report.get(2)返回一个真实的域实例。因此,命名查询充当过滤器。

到目前为止,一切都很好。命名查询与get(), list()`find()` 和 `findAll()` 以及动态查找器结合使用的方式,可能足以立即使用它们。但 Grails 1.3 还藏着另一个绝招。

链式查询

任意组合命名查询听起来怎么样?好吧,通过链接命名查询,您就可以拥有它。例如,如果您想获取 Dilbert 在本月第一周生成的所有报告,您可以调用
Report.dilbertsReports.inFirstWeek.list()

`Report.dilbertsReports().inFirstWeek()`。或者,如果您想获取伦敦服务器的任何第一周报告,您可以使用

Report.inFirstWeek.inCity("London").list()

`Report.inFirstWeek().inCity('London')`。事实上,只要它们都返回相同类型的域类,您就可以链接任意数量的命名查询。

命名查询提供了一种强大的查询重用技术,该技术实现简单且易于使用。现在,您可以拥有一个非常丰富的域模型,并且客户端代码易于阅读和理解。这有多好?

现在我想快速看一下 Grails 1.3 的其他一些功能。

其余的精彩

Grails 1.3 版本还包含了一些更小但仍然有用的功能。其中最重要的是升级到 Groovy 1.7(Grails 1.2 及更早版本基于 Groovy 1.6)。

Groovy 1.7

Groovy 1.7 版本包含许多修复和增强功能,但也许其中两个对 Grails 开发人员来说是最重要的:
  1. 支持匿名类和内部类——因此与 Wicket 等框架的集成应该会容易得多。
  2. Power asserts——您现在可以使用 Groovy 的`assert`关键字,而不是 JUnit/TestNG 的替代方案,以获得有关断言失败原因的令人印象深刻的诊断信息。我最喜欢的新功能!

脏检查

不,这与家务活无关!正如你们中的许多人所知,Hibernate 会自动检查域实例是否已被修改,并在会话结束时持久化更改。GORM 现在允许您通过`isDirty()`方法
def book = Book.get(10)
assert !book.dirty

book.title = "Unknown"
assert book.dirty
assert book.isDirty("title")
assert !book.isDirty("author")

方法访问此功能。看看您还可以检查单个字段是否已被修改?

全局布局

正如您可能知道的,Grails 允许您通过 `` 标签或约定显式地将布局应用于视图。它不允许您为视图指定一个默认布局作为备选方案。这个缺陷现在得到了纠正,您可以通过 Config.groovy 中的设置<meta>或通过创建文件
grails.sitemesh.default.layout = 'defaultLayout'

`grails-app/views/layouts/application.gsp`来指定默认布局。第一种方法将从`grails-app/views/layouts/defaultLayout.gsp`加载布局。.

JUnit 4

对于所有热衷于测试的开发者,Grails 现在默认包含 JUnit 4,因此您可以随心所欲地为测试用例添加注解。

至此,我将结束本期 Grails 1.3 功能的介绍。希望您能充分利用命名查询!下次,我将介绍就地插件。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有