查詢方法

本節提供了一些關於 Spring Data JDBC 實現和使用的具體資訊。

您通常在儲存庫上觸發的大多數資料訪問操作都會導致針對資料庫執行查詢。定義此類查詢只需在儲存庫介面上宣告一個方法,如以下示例所示

具有查詢方法的 PersonRepository
interface PersonRepository extends PagingAndSortingRepository<Person, String> {

    List<Person> findByFirstname(String firstname);                                   (1)

    List<Person> findByFirstnameOrderByLastname(String firstname, Pageable pageable); (2)

    Slice<Person> findByLastname(String lastname, Pageable pageable);                 (3)

    Page<Person> findByLastname(String lastname, Pageable pageable);                  (4)

    Person findByFirstnameAndLastname(String firstname, String lastname);             (5)

    Person findFirstByLastname(String lastname);                                      (6)

    @Query("SELECT * FROM person WHERE lastname = :lastname")
    List<Person> findByLastname(String lastname);                                     (7)
    @Query("SELECT * FROM person WHERE lastname = :lastname")
    Stream<Person> streamByLastname(String lastname);                                     (8)

    @Query("SELECT * FROM person WHERE username = :#{ principal?.username }")
    Person findActiveUser();															(9)
}
1 該方法顯示了查詢所有具有給定 firstname 的人員。查詢是透過解析方法名稱中可以與 AndOr 連線的約束來派生的。因此,方法名稱產生了一個查詢表示式 SELECT … FROM person WHERE firstname = :firstname
2 使用 Pageable 將偏移量和排序引數傳遞給資料庫。
3 返回 Slice<Person>。選擇 LIMIT+1 行以確定是否有更多資料可供消費。不支援 ResultSetExtractor 自定義。
4 執行分頁查詢,返回 Page<Person>。僅選擇給定頁面範圍內的資料,並可能進行計數查詢以確定總計數。不支援 ResultSetExtractor 自定義。
5 根據給定條件查詢單個實體。如果結果不唯一,則以 IncorrectResultSizeDataAccessException 完成。
6 與 <3> 不同,即使查詢產生更多結果文件,第一個實體也始終會發出。
7 findByLastname 方法顯示了查詢所有具有給定 lastname 的人員。
8 streamByLastname 方法返回一個 Stream,它使得值一旦從資料庫返回就可以使用。
9 您可以使用 Spring Expression Language 動態解析引數。在此示例中,Spring Security 用於解析當前使用者的使用者名稱。

下表顯示了查詢方法支援的關鍵字

表 1. 支援的查詢方法關鍵字
關鍵字 示例 邏輯結果

After

findByBirthdateAfter(Date date)

birthdate > date

GreaterThan

findByAgeGreaterThan(int age)

age > age

GreaterThanEqual

findByAgeGreaterThanEqual(int age)

age >= age

Before

findByBirthdateBefore(Date date)

birthdate < date

LessThan

findByAgeLessThan(int age)

age < age

LessThanEqual

findByAgeLessThanEqual(int age)

年齡 <= 年齡

Between

findByAgeBetween(int from, int to)

年齡 介於 起始 AND 結束 之間

不介於

findByAgeNotBetween(int from, int to)

年齡 不介於 起始 AND 結束 之間

In

findByAgeIn(Collection<Integer> ages)

年齡 在 (年齡1, 年齡2, 年齡N) 中

NotIn

findByAgeNotIn(Collection ages)

年齡 不在 (年齡1, 年齡2, 年齡N) 中

IsNotNull, NotNull

findByFirstnameNotNull()

firstname 非空

IsNull, Null

findByFirstnameNull()

firstname 為空

Like, StartingWith, EndingWith

findByFirstnameLike(String name)

firstname 像 name

NotLike, IsNotLike

findByFirstnameNotLike(String name)

firstname 不像 name

字串上的 Containing

findByFirstnameContaining(String name)

firstname 像 '%' + name + '%'

NotContaining 用於 String

findByFirstnameNotContaining(String name)

firstname 不像 '%' + name + '%'

(無關鍵字)

findByFirstname(String name)

firstname = name

Not

findByFirstnameNot(String name)

firstname != name

IsTrue, True

findByActiveIsTrue()

active 為真

IsFalse, False

findByActiveIsFalse()

active 為假

查詢派生僅限於可以在 WHERE 子句中不使用聯接的屬性。

查詢查詢策略

JDBC 模組支援在 @Query 註解中將查詢手動定義為字串,或在屬性檔案中定義為命名查詢。

從方法名稱派生查詢目前僅限於簡單屬性,即直接存在於聚合根中的屬性。此外,此方法僅支援選擇查詢。

使用 @Query

以下示例顯示瞭如何使用 @Query 宣告查詢方法

使用 @Query 宣告查詢方法
interface UserRepository extends CrudRepository<User, Long> {

    @Query("select firstName, lastName from User u where u.emailAddress = :email")
    User findByEmailAddress(@Param("email") String email);
}

預設情況下,為了將查詢結果轉換為實體,使用的 RowMapper 與 Spring Data JDBC 自身生成的查詢相同。您提供的查詢必須與 RowMapper 期望的格式匹配。必須提供實體建構函式中使用的所有屬性的列。透過 setter、wither 或欄位訪問設定的屬性的列是可選的。結果中沒有匹配列的屬性將不會被設定。該查詢用於填充聚合根、嵌入式實體和一對一關係,包括儲存和載入為 SQL 陣列型別的原始型別陣列。將為對映、列表、集合和實體陣列生成單獨的查詢。

一對一關係的屬性名稱必須以關係名稱加 _ 為字首。例如,如果上述示例中的 User 具有一個包含屬性 cityaddress,則該 city 的列必須標記為 address_city

請注意,基於字串的查詢不支援分頁,也不接受 SortPageRequestLimit 作為查詢引數,因為對於這些查詢,需要重寫查詢。如果您想應用限制,請使用 SQL 表達此意圖並自行將相應的引數繫結到查詢。

查詢可能包含 SpEL 表示式。有兩種變體,它們的評估方式不同。

在第一種變體中,SpEL 表示式以 : 為字首,並像繫結變數一樣使用。這樣的 SpEL 表示式將被替換為繫結變數,並且該變數將繫結到 SpEL 表示式的結果。

在查詢中使用 SpEL
@Query("SELECT * FROM person WHERE id = :#{#person.id}")
Person findWithSpEL(PersonRef person);

這可以用於訪問引數的成員,如上述示例所示。對於更復雜的用例,可以在應用程式上下文中提供 EvaluationContextExtension,這反過來可以使任何物件在 SpEL 中可用。

另一種變體可以在查詢的任何位置使用,並且評估查詢的結果將替換查詢字串中的表示式。

在查詢中使用 SpEL
@Query("SELECT * FROM #{tableName} WHERE id = :id")
Person findWithSpEL(PersonRef person);

它在第一次執行之前評估一次,並使用添加了兩個變數 tableNamequalifiedTableNameStandardEvaluationContext。當表名本身是動態的,因為它們也使用 SpEL 表示式時,這種用法最有用。

Spring 完全支援基於 -parameters 編譯器標誌的 Java 8 引數名稱發現。透過在構建中使用此標誌作為除錯資訊的替代,您可以省略命名引數的 @Param 註解。
Spring Data JDBC 僅支援命名引數。

命名查詢

如果如上一節所述的註解中未提供查詢,Spring Data JDBC 將嘗試定位命名查詢。有兩種方式可以確定查詢的名稱。預設方式是獲取查詢的領域類,即儲存庫的聚合根,獲取其簡單名稱,並追加用 . 分隔的方法名稱。另外,@Query 註解有一個 name 屬性,可用於指定要查詢的查詢名稱。

命名查詢預計會在 classpath 上的屬性檔案 META-INF/jdbc-named-queries.properties 中提供。

可以透過設定 @EnableJdbcRepositories.namedQueriesLocation 的值來更改該檔案的位置。

命名查詢的處理方式與透過註解提供的查詢相同。

自定義查詢方法

流式結果

當您將 Stream 指定為查詢方法的返回型別時,Spring Data JDBC 會在元素可用時立即返回它們。在處理大量資料時,這適用於降低延遲和記憶體需求。

流包含一個到資料庫的開放連線。為避免記憶體洩漏,該連線最終需要透過關閉流來關閉。推薦的做法是使用 try-with-resource 語句。這也意味著,一旦到資料庫的連線關閉,流將無法獲取更多元素,並可能丟擲異常。

自定義 RowMapperResultSetExtractor

@Query 註解允許您指定要使用的自定義 RowMapperResultSetExtractor。屬性 rowMapperClassresultSetExtractorClass 允許您指定要使用的類,這些類將使用預設建構函式例項化。或者,您可以將 rowMapperClassRefresultSetExtractorClassRef 設定為 Spring 應用程式上下文中的 bean 名稱。

如果您不想僅為單個方法使用特定的 RowMapper,而是為所有具有返回特定型別的自定義查詢的方法使用,您可以註冊一個 RowMapperMap bean 併為每個方法返回型別註冊一個 RowMapper。以下示例演示瞭如何註冊 DefaultQueryMappingConfiguration

@Bean
QueryMappingConfiguration rowMappers() {
    return new DefaultQueryMappingConfiguration()
        .register(Person.class, new PersonRowMapper())
        .register(Address.class, new AddressRowMapper());
}

在確定要用於方法的 RowMapper 時,根據方法的返回型別,遵循以下步驟

  1. 如果型別是簡單型別,則不使用 RowMapper

    相反,查詢預計會返回具有單個列的單行,並將轉換應用於該值以匹配返回型別。

  2. QueryMappingConfiguration 中的實體類會被迭代,直到找到一個作為所討論返回型別的超類或介面的實體類。將使用為該類註冊的 RowMapper

    迭代按註冊順序進行,因此請務必在特定型別之後註冊更通用型別。

如果適用,包裝器型別(例如集合或 Optional)將被解包。因此,Optional<Person> 的返回型別在上述過程中使用 Person 型別。

透過 QueryMappingConfiguration@Query(rowMapperClass=…) 使用自定義 RowMapper,或使用自定義 ResultSetExtractor 會停用實體回撥和生命週期事件,因為結果對映可以在需要時發出自己的事件/回撥。

修改查詢

您可以使用查詢方法上的 @Modifying 將查詢標記為修改查詢,如以下示例所示

@Modifying
@Query("UPDATE DUMMYENTITY SET name = :name WHERE id = :id")
boolean updateName(@Param("id") Long id, @Param("name") String name);

您可以指定以下返回型別

  • void

  • int(更新記錄數)

  • boolean(記錄是否已更新)

修改查詢直接針對資料庫執行。不會呼叫任何事件或回撥。因此,如果帶有審計註解的欄位未在註解查詢中更新,它們也不會被更新。

© . This site is unofficial and not affiliated with VMware.