最近在學MongoDB的是時候,發現如下:
public interface CommentRepository extends MongoRepository<Comment,String > {
Page<Comment> findByParentid(String parentid, Pageable pageable);
}
這個方法就可以直接用了,也是根據parentid查詢的且分頁的結果,就感覺特別奇怪,也沒有發現xml,方法卻能執行。
原來是SpringData的命令規則,之前沒有深入學過,於是趕緊理解一下。
方法命名規則查詢
顧名思義,方法命名規則查詢就是根據方法的名字,就能創建查詢。只需要按照SpringData JPA提供的方法。
命名規則定義方法的名稱,就可以完成查詢工作。
SpringData JPA在程序執行的時候會根據方法名稱進行解析,並自動生成查詢語句進行查詢.
按照SpringData JPA定義的規則,查詢方法以findBy開頭,涉及條件查詢時,條件的屬性用條件關鍵字連接,要注意的是:條件屬性首字母需大寫。框架在進行方法名解析時,會先把方法名多余的前綴截取掉,然后對剩下部分進行解析 。
List<Article> findByTitle(String title);
//根據標題模糊查詢
List<Article> findByTitleLike(String title);
//根據標題和作者查詢
List<Article> findByTitleAndAuthor(String title, String author);
//根據ID范圍查詢
List<Article> findByAidBetween(Integer starAid, Integer endAid);
List<Article> findByAidLessThan(Integer endAid);
List<Article> findByAidIn(List<Integer> aids);
//根據創建時間之后查詢
List<Article> findByCreateTimeAfter(Date createTime);
關鍵字 | 例子 | 對應的JPQL語句 |
---|---|---|
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ? 2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is,Equals | findByFirstnameIs, findByFirstnameEquals | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | … where x.age ⇐ ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
關鍵字 | 例子 | 對應的JPQL語句 |
---|---|---|
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull | findByAgeIsNull | … where x.age is null |
IsNotNull,NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (parameter bound with appended %) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended %) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in %) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection age) | … where x.age not in ?1 |
TRUE | findByActiveTrue() | … where x.active = true |
FALSE | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstame) = U |
官網說明如下:
4.4. 定義查詢方法
存儲庫代理有兩種從方法名稱派生特定於 Store 的查詢的方式:
- 通過直接從方法名稱派生查詢。
- 通過使用手動定義的查詢。
可用選項取決於實際 Store。但是,必須有一個策略來決定要創建的實際查詢。下一節將介紹可用的選項。
4.4.1. 查詢查詢策略
以下策略可用於存儲庫基礎結構來解決查詢。使用 XML 配置,您可以通過query-lookup-strategy
屬性在名稱空間中配置策略。對於 Java 配置,可以使用Enable${store}Repositories
注解的queryLookupStrategy
屬性。某些數據存儲可能不支持某些策略。
CREATE
嘗試從查詢方法名稱構造特定於 Store 的查詢。通用方法是從方法名稱中刪除一組給定的眾所周知的前綴,然后解析該方法的其余部分。您可以在“ Query Creation”中閱讀有關查詢構造的更多信息。USE_DECLARED_QUERY
嘗試查找已聲明的查詢,如果找不到則拋出異常。該查詢可以通過某處的 Comments 定義,也可以通過其他方式聲明。請查閱特定 Store 的文檔以找到該 Store 的可用選項。如果存儲庫基礎結構在引導時找不到該方法的聲明查詢,則它將失敗。CREATE_IF_NOT_FOUND
(默認)組合了CREATE
和USE_DECLARED_QUERY
。它首先查找一個聲明的查詢,如果找不到聲明的查詢,它將創建一個基於名稱的自定義方法查詢。這是默認的查找策略,因此,如果未顯式配置任何內容,則使用該策略。它允許通過方法名稱快速定義查詢,還可以通過根據需要引入已聲明的查詢來自定義調整這些查詢。
4.4.2. 查詢創建
內置在 Spring Data 存儲庫基礎結構中的查詢構建器機制對於在存儲庫實體上構建約束查詢很有用。該機制從方法中剝離前綴find…By
,read…By
,query…By
,count…By
和get…By
,並開始解析其余部分。 Introduction 子句可以包含其他表達式,例如Distinct
,以在要創建的查詢上設置不同的標志。但是,第一個By
充當分隔符,以指示實際標准的開始。在最基本的級別上,您可以定義實體屬性的條件,並將它們與And
和Or
串聯。以下示例顯示了如何創建許多查詢:
例子 13.從方法名查詢創建
public interface PersonRepository extends Repository<User, Long> {
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
// Enables the distinct flag for the query
List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);
// Enabling ignoring case for an individual property
List<Person> findByLastnameIgnoreCase(String lastname);
// Enabling ignoring case for all suitable properties
List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
// Enabling static ORDER BY for a query
List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}
解析該方法的實際結果取決於您為其創建查詢的持久性存儲。但是,需要注意一些一般事項:
- 表達式通常是屬性遍歷,並帶有可串聯的運算符。您可以將屬性表達式與
AND
和OR
結合使用。您還將獲得對屬性表達式的支持,例如Between
,LessThan
,GreaterThan
和Like
。支持的運算符可能因數據存儲而異,因此請參考參考文檔的相應部分。 - 方法解析器支持為單個屬性(例如
findByLastnameIgnoreCase(…)
)或支持忽略大小寫的類型的所有屬性(通常為String
instance_,例如findByLastnameAndFirstnameAllIgnoreCase(…)
)設置IgnoreCase
標志。是否支持忽略大小寫可能因 Store 而異,因此請參考參考文檔中有關 Store 特定查詢方法的相關部分。 - 您可以通過將
OrderBy
子句附加到引用屬性的查詢方法並提供排序方向(Asc
或Desc
)來應用靜態排序。要創建支持動態排序的查詢方法,請參閱“ 特殊參數處理”。
4.4.3. 屬性表達式
如上例所示,屬性表達式只能引用被管實體的直接屬性。在查詢創建時,您已經確保已解析的屬性是托管域類的屬性。但是,您也可以通過遍歷嵌套屬性來定義約束。考慮以下方法簽名:
List<Person> findByAddressZipCode(ZipCode zipCode);
假設Person
具有Address
和ZipCode
。在這種情況下,該方法將創建屬性遍歷x.address.zipCode
。解析算法首先將整個部分(AddressZipCode
)解釋為屬性,然后在域類中檢查具有該名稱的屬性(未大寫)。如果算法成功,它將使用該屬性。如果不是,該算法將駝峰案例部分的源從右側分為頭和尾,並嘗試找到相應的屬性,在我們的示例中為AddressZip
和Code
。如果該算法找到了具有該頭部的屬性,則將其取為尾部,並 continue 從此處開始構建樹,以剛才描述的方式將尾部向上拆分。如果第一個分割不匹配,則算法將分割點移到左側(Address
,ZipCode
)並 continue。
盡管這在大多數情況下應該可行,但算法可能會選擇錯誤的屬性。假設Person
類也具有addressZip
屬性。該算法將在第一輪拆分中已經匹配,選擇錯誤的屬性,然后失敗(因為addressZip
的類型可能沒有code
屬性)。
要解決這種歧義,您可以在方法名稱中使用_
來手動定義遍歷點。因此,我們的方法名稱如下:
List<Person> findByAddress_ZipCode(ZipCode zipCode);
因為我們將下划線字符視為保留字符,所以我們強烈建議您遵循以下標准 Java 命名約定(即,在屬性名稱中不使用下划線,而使用駝峰大小寫)。
4.4.4. 特殊參數處理
要處理查詢中的參數,請定義方法參數,如前面的示例所示。除此之外,基礎架構還可以識別某些特定類型(例如Pageable
和Sort
),以將分頁和排序動態應用於您的查詢。下面的示例演示了這些功能:
例子 14.在查詢方法中使用Pageable
,Slice
和Sort
Page<User> findByLastname(String lastname, Pageable pageable);
Slice<User> findByLastname(String lastname, Pageable pageable);
List<User> findByLastname(String lastname, Sort sort);
List<User> findByLastname(String lastname, Pageable pageable);
第一種方法使您可以將org.springframework.data.domain.Pageable
實例傳遞給查詢方法,以將分頁動態添加到靜態定義的查詢中。 Page
知道可用元素和頁面的總數。它是通過基礎結構觸發計數查詢來計算總數來實現的。由於這可能很昂貴(取決於使用的 Store),因此您可以返回Slice
。 Slice
僅知道下一個Slice
是否可用,當遍歷較大的結果集時可能就足夠了。
排序選項也通過Pageable
實例處理。如果只需要排序,則將org.springframework.data.domain.Sort
參數添加到您的方法中。如您所見,返回List
也是可能的。在這種情況下,不會創建構建實際Page
實例所需的其他元數據(這反過來,這意味着不會發出本來必要的其他計數查詢)。而是,它將查詢限制為僅查找給定范圍的實體。
要查明整個查詢可獲得多少頁,您必須觸發另一個計數查詢。默認情況下,此查詢源自您實際觸發的查詢。
4.4.5. 限制查詢結果
可以使用first
或top
關鍵字來限制查詢方法的結果,這些關鍵字可以互換使用。可以將一個可選的數值附加到top
或first
以指定要返回的最大結果大小。如果省略該數字,則假定結果大小為 1.以下示例顯示了如何限制查詢大小:
例子 15.用Top
和First
限制查詢的結果大小
User findFirstByOrderByLastnameAsc();
User findTopByOrderByAgeDesc();
Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);
Slice<User> findTop3ByLastname(String lastname, Pageable pageable);
List<User> findFirst10ByLastname(String lastname, Sort sort);
List<User> findTop10ByLastname(String lastname, Pageable pageable);
限制表達式還支持Distinct
關鍵字。此外,對於將結果集限制為一個實例的查詢,支持使用Optional
關鍵字將結果包裝到其中。
如果將分頁或切片應用於限制查詢分頁(以及對可用頁面數的計算),則會在限制結果內應用分頁或切片。
通過使用Sort
參數來限制結果與動態排序的組合,可以讓您表達對最小的“ K”元素和對“ K”的最大元素的查詢方法。
4.4.6. 流查詢結果
通過使用 Java 8 Stream<T>
作為返回類型,可以遞增地處理查詢方法的結果。而不是將查詢結果包裝在Stream
數據存儲區中,而是使用特定於方法的方法來執行流傳輸,如以下示例所示:
例子 16.用 Java 8 Stream<T>
流式查詢的結果
@Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();
Stream<User> readAllByFirstnameNotNull();
@Query("select u from User u")
Stream<User> streamAllPaged(Pageable pageable);
Stream
可能包裝了特定於底層數據存儲的資源,因此在使用后必須將其關閉。您可以使用close()
方法或使用 Java 7 try-with-resources
塊來手動關閉Stream
,如以下示例所示:
例子 17.使用Stream<T>
導致 try-with-resources 塊
try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) {
stream.forEach(…);
}
目前,並非所有的 Spring Data 模塊都支持Stream<T>
作為返回類型。
4.4.7. 異步查詢結果
可以使用Spring 的異步方法執行能力異步運行存儲庫查詢。這意味着該方法在調用時立即返回,而實際查詢執行發生在已提交給 Spring TaskExecutor
的任務中。異步查詢執行與反應式查詢執行不同,因此不應混為一談。有關響應式支持的更多詳細信息,請參閱 Store 特定的文檔。以下示例顯示了許多異步查詢:
@Async
Future<User> findByFirstname(String firstname); (1)
@Async
CompletableFuture<User> findOneByFirstname(String firstname); (2)
@Async
ListenableFuture<User> findOneByLastname(String lastname); (3)
- (1) 使用
java.util.concurrent.Future
作為返回類型。 - (2) 使用 Java 8
java.util.concurrent.CompletableFuture
作為返回類型。 - (3) 使用
org.springframework.util.concurrent.ListenableFuture
作為返回類型。