Grails 2.0 倒數計時:持久化 (Persistence)

工程 (Engineering) | Peter Ledbrook | 2011年12月05日 | ...

距離上次的倒數部落格文章已經有一段時間了,但 2.0.0.RC3 的發布給了我一個很好的理由再寫一篇。在上一篇文章中,我重點介紹了資料庫遷移以及我們如何標準化使用新的資料庫遷移外掛程式。我將在這裡繼續持久化 (persistence) 的主題,並介紹幾個很棒的新功能,尤其是在查詢方面。

雜項 (Miscellaneous)

讓我們從一些小的改進開始。首先,抽象網域類別現在被視為大多數人所期望的那樣:抽象基底網域類別會為其及其子類別建立一個表。例如,考慮圖中顯示的層次結構。在 Grails 2 之前,這將產生單獨的 'employee' 和 'manager' 表。現在您只會得到一個 'person' 表。

Abstract Person class extended by Employee and Manager classes

不幸的是,這構成了一個重大變更,但新的行為更有意義,因此我們認為這次變更是有道理的。如果您目前正在使用抽象網域類別,那麼您可以:(1) 升級時將它們移動到 'src/groovy' 目錄中;或 (2) 遷移您的資料,例如使用資料庫遷移外掛程式。

尋找或建立 (Find or create)

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

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")

我想您會同意,這更加簡潔明瞭。

多個資料來源 (Multiple data sources)

在進入主要功能(也就是我最喜歡的功能)之前,我想指出 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()

使用者指南提供了更多關於使用者如何從使用者的角度看待其運作方式的資訊。

新的查詢語法 (New query syntax)

現在是重頭戲:新的 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(). 這兩種語法在其他方面是相同的。

上面的範例也示範了投影 (projection) 支援:能夠僅檢索單個屬性的值或聚合值,而不是網域實例本身。 因此,上面的程式碼將傳回Bookids 列表,而不是Bookinstances。 您甚至可以鏈接投影以獲得額外的靈活性。

我想強調的最後一個很酷的功能:在查詢條件中包含聚合函數非常容易。 此範例傳回所有比平均年齡大的作者

Author.where { age > avg(age) }

實際上,這只是子查詢冰山一角,因為您也可以將額外的條件應用於子查詢,如本範例所示

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

如您所見,Where 查詢以易於使用的套件提供了大量功能。 我真的認為它們將成為 Grails 應用程式中預設的查詢,取代條件查詢,並且可能會取代動態尋找器。

網域類別重新載入 (Domain class reloading)

如果 Where 查詢還不夠,最後一個功能將真正吸引 Grails 的長期使用者:透過run-app進行強大的網域類別重新載入! 在先前的 Grails 版本中,變更網域類別會導致重新啟動 servlet 容器,這可能需要一些時間。 現在,修改網域類別的行為就像任何其他類別變更一樣(包括 Java 類別在src/java中)!

我將在與下一篇 Grails 2.0 倒數部落格文章一起發布的螢幕截圖中示範這一點,但您可以隨時自己試用。 您可以新增、移除和重新命名屬性。 您甚至可以變更它們的類型。 您唯一需要注意的是,當重新載入網域類別時,dbCreate設定會生效。 如果將此設定保留為預設的「create-drop」,則資料庫中的任何現有資料都會遺失。 但是,如果您將值變更為「update」,則不會遺失任何資料,並且資料庫結構描述仍會更新。 唯一需要注意的是,「update」無法很好地處理某些類型的遷移,但在開發過程中,這通常不是問題。

Grails 第 2 版為每個人都提供了一些東西。 對於日常開發而言,Where 查詢和強大的網域類別重新載入將使其變得更容易。 對多個資料來源的支援將使需要從單個應用程式對多個關係資料庫進行操作的開發人員感到滿意。 資料庫遷移外掛程式標準化了使用關係資料庫處理生產資料遷移的方法。 所有這些都將很快提供給您!

取得 Spring 電子報

隨時關注 Spring 電子報

訂閱

領先一步

VMware 提供培訓和認證,以加速您的進度。

了解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔案。

了解更多

即將舉行的活動

查看 Spring 社群中所有即將舉行的活動。

查看全部