更多 Grails 1.3 特性

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

上週,我描述了 Grails 如何將外掛視為普通的依賴項,可以從 Maven 相容的倉庫中拉取。儘管這是 1.3 版本的主要新特性,但並非唯一。在本文中,我將介紹其他一些特性,首先是我最近才發現的一個特性。

命名查詢

GORM 提供了三種執行資料庫查詢的方式
  • 動態查詢器,例如 Book.findByTitleAndAuthorLike(...);
  • Criteria 查詢,它使用優雅的 DSL;以及
  • HQL,Hibernate 類似 SQL 的查詢語言。
這三種特性提供了易用性和強大功能的有效結合,為您提供了所需的靈活性。然而,仍然缺少一些東西。

開發一個非平凡的 Grails 應用,您很快就會意識到經常需要反覆使用相同的查詢。您該怎麼辦?複製貼上技術很簡單,但會導致主要的維護問題。您可以為每個通用查詢編寫服務方法,但這會導致服務粒度過細,並且領域模型變得相當愚蠢。這些查詢的理想位置是在領域類本身上。

這就是命名查詢的作用所在——一個在 Grails 1.2 中悄然出現的特性。

一個例子

讓我們考慮一個與報告相關的簡單領域模型:[caption id="attachment_4791" align="alignnone" width="398"]Domain model for reports[/caption]

基本思想是,一份報告可以關於一臺或多臺伺服器,並且每月可能生成一次或多次。所以dow代表“星期幾”(day of week),而wom代表“月度周次”(week of month)。每臺伺服器都有一個關聯的位置,在這個大大簡化的模型中,它僅僅是一個城市名稱。

現在考慮一下應用程式可能想從這個模型中提取什麼樣的資訊:也許是所有在月度第一週生成的報告,或者所有關於特定伺服器的報告。我們可以向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 的 criteria DSL 編寫查詢。如果您一直推遲學習 criteria 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. Power 斷言 - 現在您可以使用 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,所以您現在可以隨心所欲地註解您的測試用例了。至此,我將結束關於 Grails 1.3 特性的這一部分。希望您能好好利用命名查詢!下次,我將介紹 in-place 外掛。

獲取 Spring 新聞通訊

訂閱 Spring 新聞通訊,保持聯絡

訂閱

領先一步

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

瞭解更多

獲取支援

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

瞭解更多

即將舉辦的活動

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

檢視全部