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*()。後兩個接受一個包含域類屬性及其值的引數對映。每組也有“Create”和“Save”版本,前者表示如果域例項不存在則應建立但不儲存。後者當然會自動儲存新例項。

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

  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 從不缺少查詢選項,它支援動態查詢器、條件查詢和 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")

這意味著您可以一鍵包含或排除條件。一個可能會想到的問題是這兩個條件如何組合?預設情況下,單獨語句中的條件,例如publishDate還是author.name在這個例子中,是用隱式 AND 組合的。如果您寧願 OR 這些語句,那麼您可以使用新的whereAny()方法代替where()。除此之外,這兩種語法是相同的。

上面的例子還展示了投影支援:能夠檢索單個屬性的值或聚合值,而不是域例項本身。因此,上面的程式碼將返回一個BookID 列表,而不是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 類,順便說一句)!

我將在一個截圖影片中演示這一點,該影片將與下一篇 Countdown to Grails 2.0 部落格文章一起釋出,但請隨意自己嘗試。您可以新增、刪除和重新命名屬性。您甚至可以更改它們的型別。您唯一需要注意的是,當域類重新載入時,dbCreate設定生效。如果將其保留為預設的“create-drop”,那麼資料庫中任何現有資料都將丟失。但是,如果您將值更改為“update”,則不會丟失任何資料,並且資料庫架構仍會更新。唯一需要注意的是,“update”不能很好地處理某些型別的遷移,但在開發過程中這通常不是問題。

Grails 2 版本為每個人都帶來了一些東西。對於日常開發,Where 查詢和可靠的域類過載將使生活更輕鬆。對多個數據源的支援將令需要從單個應用程式處理多個關係資料庫的開發人員滿意。而資料庫遷移外掛則標準化了處理關係資料庫生產資料遷移的方法。所有這些都將很快與您見面!

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有