Grails 2.0 倒計時:持久化

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

自上一篇“倒計時”部落格文章以來已經過去了一段時間,但 2.0.0.RC3 的釋出給了我一個很好的理由再寫一篇。在上一篇文章中,我重點討論了資料庫遷移以及我們如何將新的 Database Migration Plugin 作為標準。在本文中,我將繼續討論持久化這一主題,並介紹一些很棒的新功能,特別是在查詢方面。

其他

讓我們從一些小的改進開始。首先,抽象域類現在按照大多數人期望的方式處理:一個抽象基域類會為其自身及其子類生成一個表。例如,考慮圖中所示的層次結構。在 Grails 2 之前,這會生成獨立的“employee”和“manager”表。現在您只會得到一個“person”表。

Abstract Person class extended by Employee and Manager classes

不幸的是,這構成了一個破壞性更改,但新的行為更加合理,因此我們認為在這種情況下這種改變是合理的。如果您目前正在使用抽象域類,那麼在升級時您可以選擇:(1) 將它們移動到 'src/groovy' 目錄中;或者 (2) 遷移您的資料,例如使用 Database Migration plugin。

查詢或建立

下一個功能簡化了一個常見的編碼模式,這在應用程式啟動時特別有用。您是否曾發現自己編寫了類似下面的程式碼?

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

這基本上是“我需要一個特定的物件,如果它不存在就建立一個”。這種模式很常見且程式碼量較大,足以證明應該為其提供專門的方法。事實上,現在有四個專門針對這種模式的方法。它們是

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

前兩個是動態findBy*()。後兩個接受包含域類屬性及其值的引數對映。每組都有“建立”(Create)和“儲存”(Save)版本,前者表示如果域例項不存在,應該建立它,但不儲存。後者當然會自動儲存新例項。

那麼,這些方法在我們的管理員角色示例中看起來是什麼樣的呢?

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

我想您會同意,這要簡潔和資訊量大得多。

多資料來源

在介紹主要功能(也就是我最喜歡的功能)之前,我想先指出一點,Burt Beckwith 的 Datasources plugin 現在已被合併到 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 在查詢方面並不缺乏選擇,支援動態查詢器、條件查詢和 HQL,因此您可能會想,為什麼我們還要引入另一種查詢方式。嗯,條件查詢功能強大,但語法可能令人困惑。HQL 通常用於更高階的用法。動態查詢器僅適用於最簡單的場景,並且不允許您查詢關聯關係。Where 查詢透過引入一種對程式設計師來說更自然的語法,同時提供與條件查詢幾乎一樣強大的功能,彌合了動態查詢器和條件查詢/SQL 之間的差距。

讓我們來看一個簡單的例子

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

這個查詢展示了幾個強大的功能,但首先吸引我的是它的可讀性。作為 Groovy 或 Java 開發者,我能認出條件中使用的運算子及其組合方式。我可以很快地知道上面的查詢會返回作者姓名以“Stephen”開頭且出版日期在 2000 年之後的書籍。即使沒有閱讀任何關於 Where 查詢語法的文件!

除了上述查詢的可讀性之外,請注意用於條件的運算子(=~ 對映到 SQL 中的 'ilike')、用於組合條件的邏輯運算子(&& 和 ||)以及list()實際執行查詢的 方法。這些邏輯運算子對我來說真是太方便了,因為我發現它們比條件查詢中的 'and' 和 'or' 塊更容易閱讀和理解。

的重要性在於list()方法是DomainClass.where()返回所謂的“分離式查詢”,這意味著您可以一次又一次地重用它。您甚至可以使用動態查詢器來代替list()以進一步過濾結果。事實上,也可以使用標準條件查詢語法獲得分離式查詢——《使用者指南》對此有更多資訊。

由於 Where 查詢接受一個閉包,您可以做更高階的事情,例如在條件中包含 Groovy 條件

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

這意味著您可以輕鬆地包含或排除條件。一個可能想到的問題是,兩個條件是如何組合的?預設情況下,獨立語句中的條件,例如publishDateauthor.name在此示例中,透過隱式 AND 組合。如果您想使用 OR 組合這些語句,則可以使用新的whereAny()方法來代替where()。除此以外,兩者語法相同。

上面的例子還演示了投影支援:能夠只檢索單個屬性的值或聚合值,而不是域例項本身。因此,上面的程式碼將返回一個列表,包含Book的 ID,而不是Book例項。您甚至可以鏈式呼叫投影以獲得額外的靈活性。

最後,我想強調一個很酷的功能:在查詢條件中包含聚合函式非常容易。這個例子返回所有年齡大於平均年齡的作者

Author.where { age > avg(age) }

事實上,這只是子查詢冰山一角,您還可以對子查詢應用額外的條件,如此示例所示

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

如您所見,Where 查詢在一個易於使用的包中提供了大量功能。我真誠地認為它們將成為 Grails 應用程式中的預設查詢方式,取代條件查詢,並有可能取代動態查詢器。

域類熱載入

如果 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 社群中所有即將舉行的活動。

檢視全部