續上文
1.4、定義方法
存儲庫代理有兩種方法可以從方法名稱派生特定於存儲的查詢。它可以直接從方法名稱派生查詢,或者使用手動定義的查詢。可用選項取決於實際store。但是,必須有一個策略來決定創建什么樣的實際查詢。我們來看看可用的選項。
1.4.1、查詢策略
以下策略可用於存儲庫基礎結構來解析查詢。在配置XML配置的情況下,您可以通過query-lookup-strategy屬性在命名空間配置策略,或者在Java配置的情況下通過Enable $ {store}存儲庫注釋的queryLookupStrategy屬性來配置策略。某些策略可能不支持特定的數據存儲。
CREATE:嘗試從查詢方法名稱構造特定於store的查詢。一般的方法是從方法名稱中移除一組已知的前綴,並解析該方法的其余部分。詳細看1.4.2
USE_DECLARED_QUERY會嘗試查找已聲明的查詢,並在發現異常時會拋出異常。查詢可以通過某處的注釋或其他方式聲明。查閱特定Store的文檔以查找該Store的可用選項。如果存儲庫基礎結構在引導時未找到該方法的已聲明查詢,則會失敗。
CREATE_IF_NOT_FOUND(默認)結合了CREATE和USE_DECLARED_QUERY。它首先查找已聲明的查詢,如果未找到已聲明的查詢,則會創建一個基於自定義方法名稱的查詢。這是默認的查找策略,因此如果您沒有明確配置任何內容,將會使用它。它允許通過方法名稱進行快速查詢定義,還可以根據需要引入已聲明的查詢來自定義這些查詢。
1.4.2、查詢創建
構建到Spring Data存儲庫基礎結構中的查詢構建器機制對構建存儲庫實體上的約束查詢非常有用。該機制剝離前綴find... By,read ... By,query ... By,count ... By,get... By方法並開始解析其余部分。引入子句可以包含更多表達式,例如在要創建的查詢上設置不同的標志。但是,第一個By作為分隔符來指示實際標准的開始。在非常基本的層次上,您可以定義實體屬性的條件,並將它們與And和Or連接起來。
示例、從方法名稱創建查詢

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等運算符的支持,就像屬性表達式一樣。
方法解析器支持為各個屬性設置IgnoreCase標志(例如,findByLastnameIgnoreCase(...))或支持忽略大小寫的類型的所有屬性(通常是String實例,例如findByLastnameAndFirstnameAllIgnoreCase(...))
您可以通過將OrderBy子句附加到引用屬性的查詢方法並提供排序方向(Asc或Desc)來應用靜態排序。
1.4.3、屬性表達式
屬性表達式只能引用被管實體的直接屬性,如上例所示。在查詢創建時,您已經確保解析的屬性是托管域類的屬性。但是,您也可以通過遍歷嵌套屬性來定義約束。假設一個人有一個ZipCode的地址。在這種情況下,方法名稱為
List<Person> findByAddressZipCode(ZipCode zipCode);
創建屬性遍歷x.address.zipCode。解析算法首先將整個部分(AddressZipCode)解釋為屬性,然后檢查domain類是否具有該名稱的屬性(未包含大小)。如果算法成功,則使用該屬性。如果不是這樣,該算法將來自右側的駱駝案件部分的來源拆分為頭部和尾部,並嘗試查找相應的屬性,在我們的示例AddressZip和Code中。如果算法找到具有該頭部的屬性,它將采用尾部並從該處繼續構建樹,然后按照剛剛描述的方式分割尾部。如果第一次拆分不匹配,則算法將拆分點移到左側(地址,ZipCode)並繼續。
雖然這應該適用於大多數情況,但算法可能會選擇錯誤的屬性。假設Person類也有addressZip屬性。該算法將在第一輪拆分中匹配,並且基本上選擇錯誤的屬性並最終失敗(因為addressZip的類型可能沒有代碼屬性)。
要解決這種歧義,您可以在方法名稱中使用\ _手動定義遍歷點。所以我們的方法名稱會像這樣結束:
List<Person> findByAddress_ZipCode(ZipCode zipCode);
當我們將下划線看作保留字符時,我們強烈建議遵循標准的Java命名約定(即不要在屬性名稱中使用下划線,而應使用駱駝大小寫)。
1.4.4、特殊參數處理
要處理查詢中的參數,只需定義方法參數,如上例中已經看到的那樣。除此之外,基礎結構還會識別某些特定類型(如Pageable和Sort),以便動態地將分頁和排序應用於查詢。
示例、使用Pageable,Slice和Sort in查詢方法

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實例所需的附加元數據(這又意味着額外的計數查詢本來是必需的而不是被發出)而只是簡單地限制查詢只查找給定范圍的實體。
要找出完整查詢得到的頁數,您必須觸發額外的計數查詢。默認情況下,此查詢將從您實際觸發的查詢中派生。
1.4.5、限定條數查詢
查詢方法的結果可以通過關鍵字first或top來限制,這些關鍵字可以互換使用。一個可選的數字值可以被附加到top / first,以指定要返回的最大結果大小。如果該數值被忽略,則假定結果大小為1。
示例、用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關鍵字。此外,對於將結果集限制為一個實例的查詢,支持將結果包裝為可選。
如果將分頁或切片應用於限制查詢分頁(以及計算可用頁面的數量),則將其應用於有限的結果中。
注意,通過Sort參數與動態排序結合限制結果,可以表達“K”最小以及“K”最大元素的查詢方法。
1.4.6、流查詢
查詢方法的結果可以通過使用Java 8 Stream <T>作為返回類型進行遞增處理。而不是簡單地將查詢結果包裝到Stream數據存儲中,而是使用特定的方法來執行流式傳輸。
示例、用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塊。
示例、try-with-resources塊關閉 Stream <T>流
try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) { stream.forEach(…); }
並非所有的Spring Data模塊當前都支持Stream <T>作為返回類型。
1.4.7、異步查詢Async
使用Spring的異步方法執行功能可以異步執行store查詢。這意味着方法將在調用時立即返回,並且實際的查詢執行將發生在已提交給Spring TaskExecutor的任務中。

//java.util.concurrent.Future @Async Future<User> findByFirstname(String firstname); //Java 8 java.util.concurrent.CompletableFuture @Async CompletableFuture<User> findOneByFirstname(String firstname); //org.springframework.util.concurrent.ListenableFuture @Async ListenableFuture<User> findOneByLastname(String lastname);
1.5、創建repository實例
將為定義的存儲庫接口創建實例和bean定義。一種方法是使用每個支持存儲庫機制的Spring Data模塊附帶的Spring命名空間,盡管我們通常推薦使用Java-Config樣式配置。
1.5.1、XML配置
每個Spring Data模塊都包含一個repositories元素,它允許您簡單地定義Spring為您掃描的基本包。
示例、通過XML啟用Spring Data存儲庫

<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> <repositories base-package="com.acme.repositories" /> </beans:beans>
在前面的例子中,Spring被指示掃描com.acme.repositories及其所有子包,用於擴展Repository或其子接口之一的接口。對於找到的每個接口,基礎設施注冊持久性技術特定的FactoryBean以創建處理調用查詢方法的適當代理。每個bean都是在從接口名稱派生的bean名稱下注冊的,因此UserRepository的接口將注冊在userRepository下。base-package屬性允許使用通配符,以便您可以定義掃描包的模式。
使用filters
示例、使用排除過濾器元素

<repositories base-package="com.acme.repositories"> <context:exclude-filter type="regex" expression=".*SomeRepository" /> </repositories>
這個例子排除了所有以SomeRepository結尾的接口被實例化的接口。
1.5.2、JavaConfig
可以使用特定於@ Enable $ {store} Repositories注釋在JavaConfig類上觸發

@Configuration @EnableJpaRepositories("com.acme.repositories") class ApplicationConfiguration { @Bean EntityManagerFactory entityManagerFactory() { // … } }
1.5.3、獨立使用
您還可以使用Spring容器之外的存儲庫基礎結構,例如在CDI環境中。你的類路徑中仍然需要一些Spring庫,但通常你也可以通過編程來設置庫。提供存儲庫支持的Spring Data模塊提供了一個您可以使用的持久性技術特定的RepositoryFactory,如下所示。
示例
RepositoryFactorySupport factory = … // Instantiate factory here
UserRepository repository = factory.getRepository(UserRepository.class);
1.6、Spring Data存儲庫的自定義實現
1.6.1、定制個人存儲
首先要為定制功能定義一個片段接口和一個實現。然后讓您的存儲庫接口從片段接口擴展。
示例、定制存儲庫功能的接口和實現

interface CustomizedUserRepository { void someCustomMethod(User user); }

class CustomizedUserRepositoryImpl implements CustomizedUserRepository { public void someCustomMethod(User user) { // Your custom implementation } }
要找到的類的最重要的位是與片段接口相比較的名稱的Impl后綴。
實現本身不依賴於Spring Data,可以是普通的Spring bean。因此,您可以使用標准的依賴注入行為來注入對其他bean的引用
更改您的存儲庫接口
interface UserRepository extends CrudRepository<User, Long>, CustomizedUserRepository { // Declare query methods here }
讓您的存儲庫接口擴展一個片段。這樣做結合了CRUD和自定義功能,並使其可供客戶使用。
Spring數據存儲庫通過使用構成存儲庫組合的片段來實現。片段是基礎知識庫,QueryDsl和自定義接口等功能方面及其實現。每次將接口添加到存儲庫接口時,都會通過添加片段來增強組合。每個Spring Data模塊提供基礎知識庫和存儲庫方面的實現。
示例、實現片段

interface HumanRepository { void someHumanMethod(User user); } class HumanRepositoryImpl implements HumanRepository { public void someHumanMethod(User user) { // Your custom implementation } } interface EmployeeRepository { void someEmployeeMethod(User user); User anotherEmployeeMethod(User user); } class ContactRepositoryImpl implements ContactRepository { public void someContactMethod(User user) { // Your custom implementation } public User anotherContactMethod(User user) { // Your custom implementation } }
更改存儲接口

interface UserRepository extends CrudRepository<User, Long>, HumanRepository, ContactRepository { // Declare query methods here }
存儲庫可能由多個自定義實現組成,這些自定義實現按其聲明的順序導入。自定義實現具有比基本實現和存儲庫方面更高的優先級。此排序允許您覆蓋基本存儲庫和方面方法,並在兩個片段提供相同的方法簽名時解決歧義。存儲庫片段不限於在單個存儲庫接口中使用。多個存儲庫可以使用分段界面來重復使用跨不同存儲庫的定制。
示例、覆蓋 save(…)片段

interface CustomizedSave<T> { <S extends T> S save(S entity); } class CustomizedSaveImpl<T> implements CustomizedSave<T> { public <S extends T> S save(S entity) { // Your custom implementation } }
自定義

interface UserRepository extends CrudRepository<User, Long>, CustomizedSave<User> { } interface PersonRepository extends CrudRepository<Person, Long>, CustomizedSave<Person> { }
配置
如果使用命名空間配置,則存儲庫基礎結構會嘗試通過掃描我們找到存儲庫所在的軟件包下的類來自動檢測自定義實施片段。這些類需要遵循將名稱空間元素的屬性repository-impl-postfix追加到找到的碎片接口名稱的命名約定。這個后綴默認為Impl。
示例

<repositories base-package="com.acme.repository" /> <repositories base-package="com.acme.repository" repository-impl-postfix="FooBar" />
第一個配置示例將嘗試查找類com.acme.repository.CustomizedUserRepositoryImpl作為自定義存儲庫實現,而第二個示例將嘗試查找com.acme.repository.CustomizedUserRepositoryFooBar
解決歧義
如果在不同的包中找到具有匹配類名的多個實現,則Spring Data使用這些bean名來標識要使用的正確類。鑒於上面介紹的CustomizedUserRepository的以下兩個自定義實現,第一個實現將被選中。它的bean名稱是customizedUserRepositoryImpl,它與分片接口的名稱相匹配(CustomizedUserRepository)加上后綴Impl。
示例、

package com.acme.impl.one; class CustomizedUserRepositoryImpl implements CustomizedUserRepository { // Your custom implementation }

package com.acme.impl.two; @Component("specialCustomImpl") class CustomizedUserRepositoryImpl implements CustomizedUserRepository { // Your custom implementation }
如果使用@Component(“specialCustom”)對UserRepository接口進行注釋,那么bean名稱加上Impl與為com.acme.impl.two中的存儲庫實現定義的接口相匹配,它將被選中而不是第一個。
手工接入實現

<repositories base-package="com.acme.repository" /> <beans:bean id="userRepositoryImpl" class="…"> <!-- further configuration --> </beans:bean>
1.6.2、自定義基礎存儲庫
自定義存儲庫基類

class MyRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> { private final EntityManager entityManager; MyRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) { super(entityInformation, entityManager); // Keep the EntityManager around to used from the newly introduced methods. this.entityManager = entityManager; } @Transactional public <S extends T> S save(S entity) { // implementation goes here } }
最后一步是讓Spring Data基礎設施知道定制的存儲庫基類。在JavaConfig中,這是通過使用@ Enable ... Repositories注釋的repositoryBaseClass屬性來實現的:
使用JavaConfig配置定制存儲庫基類
@Configuration @EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class) class ApplicationConfiguration { … }
或者使用XML配置自定義存儲庫基類
<repositories base-package="com.acme.repository" base-class="….MyRepositoryImpl" />
1.7、從聚合根發布事件
地址:查看