更多Grails 1.3特性

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

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

命名查询

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

开发一个非平凡的 Grails 应用程序,您很快就会意识到您经常一遍又一遍地使用相同的查询。您应该怎么做?复制粘贴技术很简单,但会给您带来主要的维护问题。您可以为每个常用查询编写服务方法,但是这样最终会导致非常细粒度的服务和相当愚蠢的领域模型。这些查询的理想位置是在领域类本身。

这就是命名查询的用武之地——一个在 Grails 1.2 中悄然出现的特性。

一个例子

让我们考虑一个与报表相关的简单领域模型:[caption id="attachment_4791" align="alignnone" width="398"]报表的领域模型[/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 的标准查询 DSL 来编写查询。如果您一直推迟学习标准查询 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 还包含了另一个技巧。

链接查询

任意组合的命名查询听起来如何?好吧,通过链接命名查询,您就可以做到这一点。例如,如果我想要本月第一周生成的 Dilbert 的所有报表,我可以调用
Report.dilbertsReports.inFirstWeek.list()

或者,如果我想要关于伦敦服务器的所有第一周报表,我可以使用

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

事实上,您可以根据需要链接任意数量的命名查询,只要它们都返回相同类型的领域类即可。

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

现在我想快速了解一些其他的 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. 强大的断言——您现在可以使用 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 允许您通过<meta>标签或通过约定将布局显式应用于视图。它不允许您做的是指定视图的默认布局作为后备选项。现在已经纠正了这一缺陷,您可以通过 Config.groovy 中的设置来指定默认布局
grails.sitemesh.default.layout = 'defaultLayout'

或者通过创建文件grails-app/views/layouts/application.gsp。第一种方法将从grails-app/views/layouts/defaultLayout.gsp.

中获取布局

JUnit 4

对于所有测试爱好者来说,Grails 最终默认包含 JUnit 4,因此您现在可以根据自己的意愿注释测试用例。

获取Spring新闻通讯

通过Spring新闻通讯保持联系

订阅

领先一步

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

了解更多

获取支持

Tanzu Spring在一个简单的订阅中提供对OpenJDK™、Spring和Apache Tomcat®的支持和二进制文件。

了解更多

即将举行的活动

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

查看全部