Grails 2.0倒计时:持久化

工程 | Peter Ledbrook | 2011年12月5日 | ...

距离上次倒计时博客文章已经有一段时间了,但2.0.0.RC3的发布给了我写另一篇的充分理由。在上一篇文章中,我重点介绍了数据库迁移以及我们如何将新的数据库迁移插件标准化。我将继续讨论持久化主题,并介绍一些很棒的新功能,尤其是在查询方面。

其他

让我们从一些小的改进开始。首先,现在抽象域类按照大多数人的预期进行处理:抽象基域类会为其本身及其子类生成一个表。例如,考虑图中所示的层次结构。在Grails 2之前,这将导致单独的“employee”和“manager”表。现在你只需要一个“person”表。

Abstract Person class extended by Employee and Manager classes

不幸的是,这构成了一个重大更改,但是新的行为更有意义,因此我们认为在这种情况下这种更改是合理的。如果您当前正在使用抽象域类,则您可以:(1)在升级时将它们移动到“src/groovy”目录;或(2)迁移您的数据,例如使用数据库迁移插件。

查找或创建

下一个功能简化了常见的编码模式,这在应用程序引导程序中特别有用。您是否曾经发现自己编写了如下代码?

def adminRole = Role.findByName("admin")
if (!adminRole) {
    adminRole = new Role(name: "admin").save()
}

它基本上是“我需要一个特定的对象,但如果它不存在则创建它”。它很常见,并且冗长到足以证明使用专用方法。事实上,现在有四种方法专门用于此模式。它们是

  1. findOrCreateBy()
  2. findOrSaveBy()
  3. findOrCreateWhere()
  4. findOrSaveWhere()

前两种是动态方法的变体findBy*()。后两种接受包含域类属性及其值的map参数。每组也有“创建”和“保存”版本,前者表示如果域实例不存在,则应创建它*但不保存*。后者当然会自动保存新实例。

那么,这些方法在我们的管理员角色示例中是如何工作的呢?

  1. findOrCreateByName("admin")
  2. findOrSaveByName("admin")
  3. findOrCreateWhere(name: "admin")
  4. findOrSaveWhere(name: "admin")

我认为您会同意,这更加简洁明了。

多个数据源

在介绍主要功能(也就是我最喜欢的功能)之前,我想指出Burt Beckwith的Datasources插件现在已被合并到Grails核心代码中。这意味着只要您下载Grails,就可以开始定义多个(关系型)数据源。然后,您可以执行一些有趣的事情,例如将域类映射到特定数据库

class User {
    ...
    static mapping = {
        datasource 'auth'
    }
}

甚至可以指定用于单个检索的数据源

// Retrieve the zip code specifically from our audit DB
def zipCode = ZipCode.auditing.get(42)

和存储

// Save some zip code to the audit DB
zipCode.auditing.save()

用户指南提供了更多关于其从用户角度如何工作的信息。

新的查询语法

现在是主要事件:新的Where查询。Grails不乏查询选项,包括动态查找器、criteria查询和HQL支持,因此您可能想知道为什么我们要引入另一个查询。好吧,criteria查询功能强大,但语法可能令人困惑。HQL通常用于更高级的使用。动态查找器仅适用于最简单的场景,并且不允许您查询关系。Where查询弥合了动态查找器和criteria/SQL之间的差距,通过引入更自然的(对于程序员)语法,同时提供了几乎与criteria查询一样强大的功能。

让我们看一个简单的例子

def year2000 = ...
Book.where { author =~ "Stephen%" && publishDate > year2000 }.list()

这个查询演示了几个强大的功能,但我首先想到的是它的可读性。作为Groovy或Java开发人员,我认识到criteria中使用的运算符以及它们如何组合。我可以很快地知道上面的查询将返回作者姓名以“Stephen”开头且出版日期在2000年之后的书籍。即使没有阅读任何关于Where查询语法的文档!

除了上面查询的可读性之外,请注意criteria运算符的使用(=~映射到SQL中的'ilike')、用于组合criteria的逻辑运算符(&&和||)以及list()方法来实际执行查询。逻辑运算符对我来说是一个福音,因为我发现它们比criteria查询中的“and”和“or”块更容易阅读和理解。

的意义在于list()方法是DomainClass.where()返回所谓的脱节查询,这意味着您可以反复使用它。您甚至可以使用动态查找器代替list()来进一步过滤结果。事实上,使用标准criteria查询语法也可以使用脱节查询——同样,用户指南提供了更多关于这方面的信息。

由于Where查询采用闭包,您可以执行更高级的操作,例如在criteria中包含Groovy条件

def constrainName = true
def bookIds = Book.where {
    if (constrainName) {
        author.name =~ "Stephen%"
    }
    publishDate in start..end
}.property("id")

这意味着您可以随意包含或排除criteria。您可能会想到的一个问题是如何组合两个criteria?默认情况下,例如在publishDateauthor.name在这个例子中,使用隐式AND组合。如果您希望对语句使用OR,则可以使用新的whereAny()方法代替where()。除此之外,两种方法的语法是相同的。

上面的例子也演示了*投影*支持:能够检索单个属性的值或聚合值,而不是域实例本身。因此,上面的代码将返回一个Bookid列表,而不是Book实例。您甚至可以链接投影以获得额外的灵活性。

我想重点介绍的最后一个很酷的功能:在查询criteria中包含聚合函数非常容易。此示例返回所有年龄大于平均年龄的作者

Author.where { age > avg(age) }

事实上,这只是子查询冰山一角,因为您还可以将额外criteria应用于子查询,如本例所示

def query = Person.where {
  age > avg(age).of { lastName == "Simpson" } && firstName == "Homer"
}

如您所见,Where查询在一个易于使用的包中提供了大量功能。我真心认为它们将成为Grails应用程序中的默认查询,取代criteria查询并可能取代动态查找器。

域类重新加载

如果Where查询还不够,那么最终功能将真正吸引长期Grails用户:使用run-app进行强大的域类重新加载!在以前的Grails版本中,更改域类将导致servlet容器重新启动,这可能需要一些时间。现在,修改域类就像修改任何其他类一样(顺便说一下,这包括src/java中的Java类!)

我将在下一篇文章的Grails 2.0倒计时视频中演示这一点,但您可以随时自己尝试。您可以添加、删除和重命名属性。您甚至可以更改它们的类型。您需要注意的唯一一点是,当域类重新加载时,dbCreate设置将生效。如果将其保留为默认值“create-drop”,则数据库中的所有现有数据都将丢失。但是,如果您将其值更改为“update”,则不会丢失任何数据,并且数据库模式仍然会更新。“update”处理某些类型的迁移不是很好,但在开发过程中这通常不是问题。

Grails 2版本对每个人都有好处。对于日常开发,Where查询和强大的域类重新加载将使生活更轻松。对多个数据源的支持将使需要从单个应用程序处理多个关系数据库的开发人员感到满意。数据库迁移插件标准化了处理关系数据库生产数据迁移的方法。所有这一切都将很快到来!

获取Spring新闻

通过Spring新闻保持联系

订阅

领先一步

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

了解更多

获得支持

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

了解更多

即将举行的活动

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

查看全部