查詢方法
您通常在 repository 上觸發的大多數資料訪問操作都會導致針對資料庫執行查詢。定義此類查詢的方法是在 repository 介面上宣告一個方法,如下例所示
interface ReactivePersonRepository extends ReactiveSortingRepository<Person, Long> {
Flux<Person> findByFirstname(String firstname); (1)
Flux<Person> findByFirstname(Publisher<String> firstname); (2)
Flux<Person> findByFirstnameOrderByLastname(String firstname, Pageable pageable); (3)
Mono<Person> findByFirstnameAndLastname(String firstname, String lastname); (4)
Mono<Person> findFirstByLastname(String lastname); (5)
@Query("SELECT * FROM person WHERE lastname = :lastname")
Flux<Person> findByLastname(String lastname); (6)
@Query("SELECT firstname, lastname FROM person WHERE lastname = $1")
Mono<Person> findFirstByLastname(String lastname); (7)
}
1 | 該方法展示了根據給定的 firstname 查詢所有人員。該查詢是透過解析方法名,查詢可以用 And 和 Or 連線的約束來派生的。因此,方法名會生成一個查詢表示式 SELECT … FROM person WHERE firstname = :firstname 。 |
2 | 該方法展示了根據給定的 firstname 查詢所有人員,一旦 firstname 由給定的 Publisher 發出。 |
3 | 使用 Pageable 向資料庫傳遞偏移量和排序引數。 |
4 | 根據給定的條件查詢單個實體。如果結果不唯一,則丟擲 IncorrectResultSizeDataAccessException 異常。 |
5 | 除非 <4>,即使查詢產生更多結果行,也始終發出第一個實體。 |
6 | findByLastname 方法展示了根據給定的姓氏查詢所有人員。 |
7 | 查詢單個 Person 實體,僅投影 firstname 和 lastname 列。帶註解的查詢使用原生繫結標記,在此示例中為 Postgres 繫結標記。 |
請注意,在 @Query
註解中使用的 select 語句的列必須與 NamingStrategy
為相應屬性生成的名稱匹配。如果 select 語句不包含匹配的列,則該屬性不會被設定。如果持久化建構函式需要該屬性,則會提供 null 或(對於原始型別)預設值。
下表顯示了查詢方法支援的關鍵字
關鍵字 | 示例 | 邏輯結果 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
修改查詢
前面的部分介紹瞭如何宣告查詢來訪問給定的實體或實體集合。使用前面表格中的關鍵字可以與 delete…By
或 remove…By
結合使用,以建立刪除匹配行的派生查詢。
interface ReactivePersonRepository extends ReactiveSortingRepository<Person, String> {
Mono<Integer> deleteByLastname(String lastname); (1)
Mono<Void> deletePersonByLastname(String lastname); (2)
Mono<Boolean> deletePersonByLastname(String lastname); (3)
}
1 | 使用返回型別 Mono<Integer> 將返回受影響的行數。 |
2 | 使用 Void 僅報告行是否成功刪除,而不發出結果值。 |
3 | 使用 Boolean 報告是否至少刪除了一行。 |
由於此方法對於全面的自定義功能是可行的,因此您可以透過使用 @Modifying
註解查詢方法來修改僅需要引數繫結的查詢,如下例所示
@Modifying
@Query("UPDATE person SET firstname = :firstname where lastname = :lastname")
Mono<Integer> setFixedFirstnameFor(String firstname, String lastname);
修改查詢的結果可以是
-
Void
(或 Kotlin 的Unit
)用於丟棄更新計數並等待完成。 -
Integer
或其他數字型別,發出受影響的行數。 -
Boolean
,用於發出是否至少更新了一行。
@Modifying
註解僅在與 @Query
註解結合使用時才相關。派生的自定義方法不需要此註解。
修改查詢直接針對資料庫執行。不會觸發任何事件或回撥。因此,帶有審計註解的欄位如果在帶註解的查詢中沒有被更新,也不會被更新。
或者,您可以使用Spring Data Repositories 的自定義實現中描述的功能來新增自定義修改行為。
使用 @Query
以下示例展示瞭如何使用 @Query
來宣告查詢方法
interface UserRepository extends ReactiveCrudRepository<User, Long> {
@Query("select firstName, lastName from User u where u.emailAddress = :email")
Flux<User> findByEmailAddress(@Param("email") String email);
}
請注意,基於 String 的查詢不支援分頁,也不接受 Sort 、PageRequest 和 Limit 作為查詢引數,因為對於這些查詢,需要重寫查詢。如果您想應用限制,請使用 SQL 表達此意圖並自行將適當的引數繫結到查詢。 |
Spring 完全支援基於 -parameters 編譯器標誌的 Java 8 引數名稱發現。在您的構建中使用此標誌作為除錯資訊的替代方案,可以省略命名引數的 @Param 註解。 |
包含 SpEL 表示式的查詢
查詢字串定義可以與 SpEL 表示式一起使用,以在執行時建立動態查詢。SpEL 表示式可以透過兩種方式使用。
SpEL 表示式可以提供斷言值 (predicate values),這些值在執行查詢之前進行評估。
表示式透過一個包含所有引數的陣列暴露方法引數。以下查詢使用 [0]
來宣告 lastname
的斷言值(等同於 :lastname
引數繫結)。
@Query("SELECT * FROM person WHERE lastname = :#{[0]}")
Flux<Person> findByQueryWithParameterExpression(String lastname);
這種表示式支援可以透過查詢 SPI(org.springframework.data.spel.spi.EvaluationContextExtension
)進行擴充套件。查詢 SPI 可以貢獻屬性和函式,並可以自定義根物件。擴充套件在構建查詢時,在 SpEL 評估時從應用上下文中檢索。
將 SpEL 表示式與普通引數結合使用時,請使用命名引數表示法而非原生繫結標記,以確保正確的繫結順序。 |
使用表示式的另一種方法是在查詢中間使用,與引數無關。評估表示式的結果將替換查詢字串中的表示式。
@Query("SELECT * FROM #{#tableName} WHERE lastname = :lastname")
Flux<Person> findByQueryWithExpression(String lastname);
它在第一次執行前評估一次,並使用一個添加了 tableName
和 qualifiedTableName
兩個變數的 StandardEvaluationContext
。當表名本身是動態的(因為它們也使用 SpEL 表示式)時,這種用法最有用。
在查詢字串中使用 SpEL 是增強查詢的強大方法。但是,它們也可能接受各種不必要的引數。在將字串傳遞給查詢之前,您應該確保對其進行淨化(sanitize),以避免對查詢造成不必要的更改。