Spring Data Commons 官方文檔學習

Spring Data Commons 官方文檔學習   -by LarryZeal
Version 1.12.6.Release, 2017-07-27

前言

Spring Data Commons project 將core Spring concepts應用到了不少關係型和非關係型數據存儲的解決方案開發。

參考文檔

1. 依賴

因爲Spring Data modules不一樣的開發時間,因此它們大多數都有不一樣的主版本號和小版本號。想要找到兼容版本的最佳方式是依賴Spring Data Release Train BOM,已經提供了兼容版本定義。在Maven項目中,你能夠在POM的 <dependencyManagement />部分聲明該依賴:html

例子1. 使用Spring Data release train BOM
  1. <dependencyManagement>
  2. <dependencies>
  3. <dependency>
  4. <groupId>org.springframework.data</groupId>
  5. <artifactId>spring-data-releasetrain</artifactId>
  6. <version>${release-train}</version>
  7. <scope>import</scope>
  8. <type>pom</type>
  9. </dependency>
  10. </dependencies>
  11. </dependencyManagement>
當前的release train版本是 Ingalls-SR6 。 該train的名字是按照字母順序升序的,當前可用的版本羅列在這裏 here 。版本的名字遵循這種格式: ${name}-${release} ,其中release多是下列之一:
  • BUILD-SNAPSHOT - current snapshotsjava

  • M1, M2 etc. - milestonesgit

  • RC1, RC2 etc. - release candidatesgithub

  • RELEASE - GA releaseweb

  • SR1, SR2 etc. - service releases算法

你能夠在咱們的 Spring Data examples repository 找到實際使用的例子。若是是就地(in place)聲明Spring Data modules,你可能喜歡這樣:spring

例子2. 聲明一個Spring Data module依賴
  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.data</groupId>
  4. <artifactId>spring-data-jpa</artifactId>
  5. </dependency>
  6. </dependencies>
 

1.1. 使用Spring Boot 進行依賴管理

Spring Boot已經爲你選擇了很是新的Spring Data modules版本。若是你想要升級到更新的版本,只須要簡單的配置下 property spring-data-releasetrain.version to the train name and iteration ,配置成你想使用的。mongodb

1.2. Spring框架

當前版本的Spring Data modules要求Spring框架版本是4.3.10.RELEASE,或更高。這些模塊,也可能運行在老版本上面,可是,強烈推薦使用新的版本。express

2. 使用Spring Data Repositories

Spring Data repository abstraction的目標是明顯地減小呆板代碼(指加載驅動、建立/關閉連接之類的事情)的數量。

2.1. 核心概念

Spring Data repository abstraction的核心接口是 Repository 。它使用domain class以及domain class的id類型做爲類型參數來管理(見接口泛型)。該接口主要是做爲一個標記接口,來捕獲使用的類型,並幫助用戶來發現繼承自該接口的接口。 CrudRepository 爲其管理的entities提供了複雜的CRUD功能。

例子 3. CrudRepository interface
publicinterfaceCrudRepository<T, ID extendsSerializable>extendsRepository<T, ID>{<S extends T> S save(S entity);(1) T findOne(ID primaryKey);(2)Iterable<T> findAll();(3)Long count();(4)voiddelete(T entity);(5)boolean exists(ID primaryKey);(6)// … more functionality omitted.}
保存給定的 entity。
② 返回經過id查找到的entity。
③ 返回全部的entities。
④ 返回entities的數量。
⑤ 刪除給定的entity。
⑥ 根據給定的id判斷entity是否存在。
 
咱們也提供了特定持久化技術的抽象,如 JpaRepository   or   MongoRepository 。這些接口繼承自 CrudRepository   ,並暴露出底層持久化技術的能力,而不只僅是泛泛的持久化技術接口(如 CrudRepository )的能力。
 

基於CrudRepository 還有一個 PagingAndSortingRepository abstraction,添加了分頁相關的功能:

例子4. PagingAndSortingRepository
publicinterfacePagingAndSortingRepository<T, ID extendsSerializable>extendsCrudRepository<T, ID>{Iterable<T> findAll(Sort sort);Page<T> findAll(Pageable pageable);}

若是你想訪問User的第二頁(每頁20條記錄),你能夠這樣作:

PagingAndSortingRepository<User,Long> repository =// … get access to a beanPage<User> users = repository.findAll(newPageRequest(1,20));

除了查詢方法,還有count和delete查詢的衍生:

例子5. 衍生的Count查詢
publicinterfaceUserRepositoryextendsCrudRepository<User,Long>{Long countByLastname(String lastname);}
例子6. 衍生的Delete查詢
publicinterfaceUserRepositoryextendsCrudRepository<User,Long>{Long deleteByLastname(String lastname);List<User> removeByLastname(String lastname);}

2.2. Query methods 查詢方法

標準的CRUD functionality repositories,一般會查詢底層數據存儲。使用Spring Data,只須要如下四個步驟:

  1. 聲明一個接口,繼承Repository 接口或其子接口,填上entity類型和id類型(主鍵類型)。

    interfacePersonRepositoryextendsRepository<Person,Long>{}
  2. 在該接口中聲明查詢方法。

    interfacePersonRepositoryextendsRepository<Person,Long>{List<Person> findByLastname(String lastname);}
  3. 設置Spring,以建立這些接口的代理實例。可使用 JavaConfig:

    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;@EnableJpaRepositoriesclassConfig{}

    也可使用 XML configuration:

    <?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:jpa="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"><jpa:repositoriesbase-package="com.acme.repositories"/></beans>

    該例子中使用了JPA namespace。若是使用其餘存儲的repository abstraction,你須要切換到相應的namespace,例如使用 mongodb.來代替 jpa。

    還須要注意,JavaConfig沒有顯式地配置一個package。想要自定義須要掃描的package,可使用@EnableJpaRepositories(或@EnableMongodbRepositories等)註解的basePackage屬性。

  4. 獲取注入的repository實例,並使用它。

    publicclassSomeClient{@AutowiredprivatePersonRepository repository;publicvoid doSomething(){List<Person> persons = repository.findByLastname("Matthews");}}

下面的章節會詳細解釋每一步。

2.3. 定義repository接口

第一步,必須定義一個特定domain class的repository接口。該接口必須繼承Repository,並填寫domain class和ID type。若是你想暴露CRUD methods,能夠繼承 CrudRepository ,而非Repository

2.3.1. 微調repository定義

一般,你的repository接口會繼承 RepositoryCrudRepository 或者 PagingAndSortingRepository。或者,若是你不想繼承Spring Data interfaces的話,你能夠在你的repository接口上面註解 @RepositoryDefinition。                             繼承 CrudRepository 會暴露一個完整的方法集合,能夠操做你的entities。若是你只想有選擇地暴露一些方法,能夠從 CrudRepository 中複製到你的domain repository。

這容許你定義你本身的abstraction,基於Spring Data Repositories的功能。

 
例子7. 有選擇地暴露 CRUD methods
@NoRepositoryBeaninterfaceMyBaseRepository<T, ID extendsSerializable>extendsRepository<T, ID>{ T findOne(ID id); T save(T entity);}interfaceUserRepositoryextendsMyBaseRepository<User,Long>{User findByEmailAddress(EmailAddress emailAddress);}

在這裏的第一步,你定義了一個通用的base接口,暴露了 findOne(…)save(…)

注意:別忘了註解 @RepositoryDefinition

2.3.2. 多個Spring Data modules狀況下使用Repositories

在應用中僅使用一個Spring Data module,是一件很簡單的事,在定義的範圍內的全部的repository接口 都被綁定到這個Spring Data module。然而,有時候,應用須要使用多個Spring Data module。在這種狀況下,就要求一個repository定義可以區分出不一樣的持久化技術。Spring Data會進入strict repository configuration mode,由於它會在classpath中探測到多個repository factories。Strict configuration要求 repository 或 domain class的詳細信息,來決定Spring Data module綁定一個repository定義:

  1. 若是該repository定義 繼承自特定模塊的repository( extends the module-specific repository),那麼它就是特定Spring Data module的一個有效的候選。

  2. 若是該domain class 被註解了特定模塊的類註解( annotated with the module-specific type annotation),那麼它就是特定Spring Data module的一個有效的候選。Spring Data modules接受第三方註解 (如 JPA的 @Entity),或者提供本身的註解如針對 Spring Data MongoDB/Spring Data Elasticsearch @Document 註解。

例子 8. 使用特定模塊的Repository 定義
interfaceMyRepositoryextendsJpaRepository<User,Long>{}@NoRepositoryBeaninterfaceMyBaseRepository<T, ID extendsSerializable>extendsJpaRepository<T, ID>{}interfaceUserRepositoryextendsMyBaseRepository<User,Long>{}

MyRepositoryUserRepository 繼承自JpaRepository 。他們都是Spring Data JPA module的有效的候選。

例子 9. 使用泛泛的接口進行Repository定義
interfaceAmbiguousRepositoryextendsRepository<User,Long>{}@NoRepositoryBeaninterfaceMyBaseRepository<T, ID extendsSerializable>extendsCrudRepository<T, ID>{}interfaceAmbiguousUserRepositoryextendsMyBaseRepository<User,Long>{}

AmbiguousRepositoryAmbiguousUserRepository 分別繼承自 RepositoryCrudRepository 。單獨使用時沒有問題,但多個模塊並存時,就沒法區分repositories該使用哪一個模塊。

例子 10. 使用註解的Domain Classes進行Repository定義
interfacePersonRepositoryextendsRepository<Person,Long>{}@EntitypublicclassPerson{}interfaceUserRepositoryextendsRepository<User,Long>{}@DocumentpublicclassUser{}

PersonRepository 引用了帶有JPA註解 @Entity Person ,所以,該repository明顯屬於 Spring Data JPA。  UserRepository 使用了帶有 Spring Data MongoDB’s @Document 註解的 User。

例子 11. 使用代理混合註解的Domain Classes進行Repository定義
interfaceJpaPersonRepositoryextendsRepository<Person,Long>{}interfaceMongoDBPersonRepositoryextendsRepository<Person,Long>{}@Entity@DocumentpublicclassPerson{}

該例子,示意了一個同時帶有JPA註解和Spring Data MongoDB註解的domain class。它定義了兩個repositories: JpaPersonRepository and MongoDBPersonRepository。一個是用於JPA,另外一個用於MongoDB。Spring Data不能區分respositories,這會致使未定義的行爲!

Repository type detailsidentifying domain class annotations 是被用於 strict repository configuration identify repository candidates for a particular Spring Data module。 在同一個domain type上使用多個持久化技術的註解,是可能在多個持久化技術之間複用domain types,但此時 Spring Data 再也不可以決定綁定到repository的惟一的module。

區分repositories的最後一種方式是scoping repository的base packages。 Base packages 定義了掃描repository接口的出發點。默認,註解驅動的配置使用該配置類所在的package。 在 XML-based configuration中,base package 是強制填寫的。

例子 12. 註解驅動的base packages的配置
@EnableJpaRepositories(basePackages ="com.acme.repositories.jpa")@EnableMongoRepositories(basePackages ="com.acme.repositories.mongo")interfaceConfiguration{}

2.4. 定義查詢方法

repository代理,有兩種方式來從方法名中獲取一個特定存儲的查詢。它能夠從方法名中直接獲取查詢,或者,能夠經過使用一個自定義的查詢。具體可用的選項取決於具體的存儲。不管什麼方式,總有一種策略來決定建立什麼樣的實際查詢。讓咱們來看一下這些可用的選項。

2.4.1. Query 查找策略

下面的策略對於repository infrastructure來講,能夠用於解析查詢。你能夠在namespace中配置該策略,經過 query-lookup-strategy attribute (XML);也能夠在Enable ${store} Repositories 註解的 queryLookupStrategy attribute中配置(javaConfig)。一些策略可能不支持特定的數據存儲。

  • CREATE :試圖從查詢方法名中構建一個特定存儲的查詢。通常的方法是從方法中移除一個給定的你們熟知的前綴集合,並解析剩餘的部分。更多關於query construction的內容,詳見 Query 建立

  • USE_DECLARED_QUERY :試圖找到一個聲明瞭的查詢,若是找不到 會拋出異常。改查下能夠經過一個註解來定義,或者經過其餘手段聲明。查閱特定存儲的文檔,以找到可用的選項。 若是repository infrastructure在啓動期間最終沒有找到一個聲明過的查詢,就會失敗。

  • CREATE_IF_NOT_FOUND (默認):結合了 CREATEUSE_DECLARED_QUERY

2.4.2. Query 建立

Spring Data repository infrastructure中內置的query builder機制,在repository的entities上構建限制查詢(constraining queries )時頗有用。該機制從方法名中脫去了前綴 find…Byread…By query…By count…By、 以及 get…By ,並解析剩餘的部分。引入的語句,能夠包含更多表達式,如 Distinct 。然而,第一個 By 扮演了分隔符角色,標明瞭實際criteria 的開始。在一個很是基礎的級別上,你能夠定義一些條件,並使用 AndOr 來鏈接它們。

例子 13. 從方法名中建立查詢
publicinterfacePersonRepositoryextendsRepository<User,Long>{List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress,String lastname);// Enables the distinct flag for the queryList<Person> findDistinctPeopleByLastnameOrFirstname(String lastname,String firstname);List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname,String firstname);// Enabling ignoring case for an individual propertyList<Person> findByLastnameIgnoreCase(String lastname);// Enabling ignoring case for all suitable propertiesList<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname,String firstname);// Enabling static ORDER BY for a queryList<Person> findByLastnameOrderByFirstnameAsc(String lastname);List<Person> findByLastnameOrderByFirstnameDesc(String lastname);}

解析方法名的具體結果,取決於你要爲哪一個持久存儲建立查詢。不過,這裏有一些通用的事情須要注意。

  • 表達式一般遍歷property,結合操做符(可能鏈接到一塊兒)。你可使用 ANDOR拼接。你也可使用諸如 BetweenLessThanGreaterThanLike 之類的操做符。不一樣的數據存儲可能支持不一樣的操做符,因此,最好查閱相關參考文檔。

  • 方法解析器支持 IgnoreCase flag,能夠爲單獨的 properties設置 (例如, findByLastnameIgnoreCase(…)),也能夠爲全部的 properties設置 (通常是 String instances,例如, findByLastnameAndFirstnameAllIgnoreCase(…))。是否支持忽略大小寫,不一樣的存儲可能不一樣的結果,因此,請查閱相關的參考文檔。

  • 你可使用靜態排序,只須要在方法名上追加一個 OrderBy 語句,須要引用一個property,還要提供排序方式 (AscDesc)。若是想建立支持動態排序的查詢,見 特殊參數處理

2.4.3. Property expressions

Property expressions 只能指向被管理entity的直接property,前面的例子已有體現。在查詢建立時間,你已經肯定了被解析的property是被管理的domain class的一個property。然而,你還能夠爲嵌套的properties定義如今(constraints)。假定 Person 有一個 Address property,該property自己還有一個 ZipCode property。這種狀況下,下面的方法名

List<Person> findByAddressZipCode(ZipCode zipCode);

建立了 property traversal x.address.zipCode。 該解析算法,從將整個部分 (AddressZipCode)解釋爲 property 開始,檢查 domain class中是否有那個名字的 property  (uncapitalized)。若是該算法成功了,它就會使用那個property。若是失敗了,該算法會按駝峯拆分該部分 - 從右側開始,拆分紅head和tail,而後查找head相應的property,在咱們的例子中就是 AddressZip and Code。若是該算法 根據head找到了一個property,它會拿着tail繼續構建下面的分支,按照前面的方式拆分tail。若是第一個拆分不匹配,算法會將拆分點移動到左側 (Address, ZipCode),並繼續。

雖然這應該已經足夠應付大多數狀況,該算法仍然可能選擇出錯誤的property。假定 Person class 也有一個 addressZip property。該算法會在第一次拆分時就匹配,而後選擇錯誤的property,並最終失敗(由於 addressZip 的類型可能沒有 code property)。

爲了解決這種模糊,你能夠在方法名中使用 _ ,以手動地定義遍歷點。所以,咱們的方法名多是這樣的:

List<Person> findByAddress_ZipCode(ZipCode zipCode);

注意:由於咱們將下劃線_做爲保留字符,咱們強烈建議用戶的開發遵照標準Java命名慣例(例如,不在property名字中使用下劃線,而是用駝峯代替)。

2.4.4. 特殊參數處理

爲了處理在你的查詢中的參數,你能夠簡單地定義方法參數,如同上面所見到的那樣。除此以外,the infrastructure 還能夠識別特定的特殊類型,如 Pageable and 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 instance,來動態添加分頁功能。一個 Page 包含元素的總數量和可用的頁數。它能這樣,是由於 the infrastructure 觸發了一個count query,從而可以計算總數量。取決於不一樣的存儲,這可能耗費巨大,因此能夠返回Slice。一個 Slice 僅包括是否有下一個可用的 Slice

排序選項,也能夠經過 Pageable instance 處理。若是你只須要排序,那你只須要給你的方法添加一個 org.springframework.data.domain.Sort 參數。如你所見,直接返回一個 List 也是能夠的。在這種狀況下,構建 Page instance所需的元數據就不須要了(就是說不須要額外的count query了),但只能查詢給定返回的entities。

 

2.4.5. 限制查詢結果

查詢方法的結果可使用關鍵字 firsttop來限制,這兩個能夠互換。一個可選的數值能夠追加到 top/first ,以標明最大返回的數量。若是留空,默認是1。

例子 15. L使用 Top and 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

If pagination or slicing is applied to a limiting query pagination (and the calculation of the number of pages available) then it is applied within the limited result.  -- 這句嘛意思?

Note that limiting the results in combination with dynamic sorting via a Sort parameter allows to express query methods for the 'K' smallest as well as for the 'K' biggest elements.                                    

 

2.4.6. 流式查詢結果

經過使用Java 8 Stream<T> 做爲返回返回類型,查詢方法的結果能夠被加速處理。

例子 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 代碼塊。
 
例子 17. 在 try-with-resources代碼塊中使用 Stream<T> result
try(Stream<User> stream = repository.findAllByCustomQueryAndStream()){ stream.forEach(…);}
注意:目前,不是全部的Spring Data modules都支持 Stream<T> 做爲返回類型。
 

2.4.7. 異步查詢結果 (不翻譯了)

Repository queries can be executed asynchronously using Spring’s asynchronous method execution capability. This means the method will return immediately upon invocation and the actual query execution will occur in a task that has been submitted to a Spring TaskExecutor.

@AsyncFuture<User> findByFirstname(String firstname);(1)@AsyncCompletableFuture<User> findOneByFirstname(String firstname);(2)@AsyncListenableFuture<User> findOneByLastname(String lastname);(3)
① 使用 java.util.concurrent.Future 做爲返回類型。
② 使用Java 8的 java.util.concurrent.CompletableFuture 做爲返回類型。
③ 使用 org.springframework.util.concurrent.ListenableFuture 做爲返回類型。

2.5. 建立 repository instances

在本部分,你會建立 定義過的repository接口的實例和bean 定義。一種方式是使用 Spring namespace,但咱們通常都推薦使用Java-Config 形式的配置。

2.5.1. XML configuration

每一個 Spring Data module 都包含一個 repositories element,容許你簡單地定義一個base package -- Spring會掃描它。

例子 18. 啓用 Spring Data repositories -- 經過 XML
<?xml version="1.0" encoding="UTF-8"?><beans:beansxmlns: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"><repositoriesbase-package="com.acme.repositories"/></beans:beans>

在上面的例子中,Spring被配置成掃描 com.acme.repositories 和其全部子包,以查找全部繼承自 Repository 或其子接口的接口。對於每一個找到的接口,infrastructure會註冊持久化技術特定的 FactoryBean ,以建立恰當的代理,從而處理查詢方法的調用。每一個bean都被註冊了一個bean name,默認從接口名字中獲取,所以一個 UserRepository 接口,可能被註冊成 userRepositorybase-package attribute 容許通配符,因此,你還能夠定義一個pattern。

使用過濾器

默認,the infrastructure會撿起每個繼承自持久化技術特定的 Repository 及其子接口的接口 -- 要在base package之下,並建立一個bean實例。可是,你或許想要更細化的控制。這時,你須要使用 <repositories /> 中的 <include-filter /> <exclude-filter /> elements。詳見  Spring 參考文檔

例如,想要排除特定接口,你可能會使用下面的配置:

例子 19. 使用 exclude-filter element
<repositoriesbase-package="com.acme.repositories"><context:exclude-filtertype="regex"expression=".*SomeRepository"/></repositories>

該例子排除了全部以 SomeRepository 結尾的內容。

2.5.2. JavaConfig

The repository infrastructure 也能夠經過在一個JavaConfig class上使用 @Enable${store}Repositories 註解來開啓。 

下面是一個簡單的例子。

例子 20. 樣例 基於註解的repository configuration
@Configuration@EnableJpaRepositories("com.acme.repositories")classApplicationConfiguration{@BeanpublicEntityManagerFactory entityManagerFactory(){// …}}
該樣例,使用了JPA特有的註解,你可能會切換成其餘的。一樣的狀況也適用於 EntityManagerFactory   bean。

2.5.3. 單獨使用 Standalone usage

你能夠在Spring container以外使用 the repository infrastructure,例如,在CDI 環境中。你仍然須要一些Spring lib,但通常,你也能夠經過編碼設置repositories。

例子 21. 單獨使用 repository factory
RepositoryFactorySupport factory =// Instantiate factory hereUserRepository repository = factory.getRepository(UserRepository.class);

2.6. Spring Data repositories的自定義實現

常常,須要爲幾個repository方法提供一個自定義的實現。Spring Data repositories容許你提供自定義的 repository 代碼,並將其集成到 generic CRUD abstraction中。

2.6.1. 爲某個repositories 添加自定義行爲

爲了豐富一個帶有自定義功能的repository,你須要先定義一個接口,以及接口中自定義功能的實現。用你的repository 接口來繼承這個自定義的接口。

例子 22. 自定義repository功能的接口
interfaceUserRepositoryCustom{publicvoid someCustomMethod(User user);}
例子 23. 自定義repository功能的實現
classUserRepositoryImplimplementsUserRepositoryCustom{publicvoid someCustomMethod(User user){// Your custom implementation}}
注意:這裏最重要的就是 後綴Impl
該實現,不依賴於 Spring Data,能夠做爲一個常規的 Spring bean。所以,你可使用標準的依賴注入方式將其注入到其餘bean中,如 JdbcTemplate
例子 24. 更改你的基礎repository 接口
interfaceUserRepositoryextendsCrudRepository<User,Long>,UserRepositoryCustom{// Declare query methods here}

讓你的標準的 repository interface繼承這個自定義的接口。

Configuration 配置

若是你使用namespace 配置, the repository infrastructure會試着自動探測自定義的實現,經過掃描base package中的classes。這些classes,須要遵照命名慣例,即在namespace element的 attribute repository-impl-postfix 中設置的。該後綴默認是 Impl

例子 25. 配置例子
<repositoriesbase-package="com.acme.repository"/><repositoriesbase-package="com.acme.repository"repository-impl-postfix="FooBar"/>

第一個配置,會試着查找一個class: com.acme.repository.UserRepositoryImpl,將其做爲自定義的repository 實現;第二個配置,則會試着查找com.acme.repository.UserRepositoryFooBar

Manual wiring 手動注入 (略)

The approach just shown works well if your custom implementation uses annotation-based configuration and autowiring only, as it will be treated as any other Spring bean. If your custom implementation bean needs special wiring, you simply declare the bean and name it after the conventions just described. The infrastructure will then refer to the manually defined bean definition by name instead of creating one itself.

Example 26. Manual wiring of custom implementations
<repositoriesbase-package="com.acme.repository"/><beans:beanid="userRepositoryImpl"class="…"><!-- further configuration --></beans:bean>

2.6.2. 爲全部repositories 添加自定義行爲 (暫不須要,略)

The preceding approach is not feasible when you want to add a single method to all your repository interfaces. To add custom behavior to all repositories, you first add an intermediate interface to declare the shared behavior.

Example 27. An interface declaring custom shared behavior
@NoRepositoryBeanpublicinterfaceMyRepository<T, ID extendsSerializable>extendsPagingAndSortingRepository<T, ID>{void sharedCustomMethod(ID id);}

Now your individual repository interfaces will extend this intermediate interface instead of the Repository interface to include the functionality declared. Next, create an implementation of the intermediate interface that extends the persistence technology-specific repository base class. This class will then act as a custom base class for the repository proxies.

Example 28. Custom repository base class
publicclassMyRepositoryImpl<T, ID extendsSerializable>extendsSimpleJpaRepository<T, ID>implementsMyRepository<T, ID>{privatefinalEntityManager entityManager;publicMyRepositoryImpl(JpaEntityInformation entityInformation,EntityManager entityManager){super(entityInformation, entityManager);// Keep the EntityManager around to used from the newly introduced methods.this.entityManager = entityManager;}publicvoid sharedCustomMethod(ID id){// implementation goes here}}
 
  The class needs to have a constructor of the super class which the store-specific repository factory implementation is using. In case the repository base class has multiple constructors, override the one taking an EntityInformation plus a store specific infrastructure object (e.g. an EntityManager or a template class).

The default behavior of the Spring <repositories /> namespace is to provide an implementation for all interfaces that fall under the base-package. This means that if left in its current state, an implementation instance of MyRepository will be created by Spring. This is of course not desired as it is just supposed to act as an intermediary between Repository and the actual repository interfaces you want to define for each entity. To exclude an interface that extends Repository from being instantiated as a repository instance, you can either annotate it with @NoRepositoryBean (as seen above) or move it outside of the configured base-package.

The final step is to make the Spring Data infrastructure aware of the customized repository base class. In JavaConfig this is achieved by using the repositoryBaseClass attribute of the @Enable…Repositories annotation:

Example 29. Configuring a custom repository base class using JavaConfig
@Configuration@EnableJpaRepositories(repositoryBaseClass =MyRepositoryImpl.class)classApplicationConfiguration{}

A corresponding attribute is available in the XML namespace.

Example 30. Configuring a custom repository base class using XML
<repositoriesbase-package="com.acme.repository"base-class="….MyRepositoryImpl"/>

2.7. 從aggregate roots發佈事件

被repositories管理的entities,被稱做aggregate roots。在一個Domain-Driven Design 應用中,這些aggregate roots 通常會發布domain events。Spring Data提供了一個註解 @DomainEvents ,你能夠用於一個方法上,從而使得發佈事件足夠簡單。

Example 31. Exposing domain events from an aggregate root
classAnAggregateRoot{@DomainEvents //(1)Collection<Object> domainEvents(){// … return events you want to get published here}@AfterDomainEventsPublication //(2)void callbackMethod(){// … potentially clean up domain events list}}
① 該方法使用註解   @DomainEvents ,既能夠返回一個事件實例,也能夠返回事件的一個集合。注意,必須無參數。
② 當全部的事件都被髮布以後,帶有註解   @AfterDomainEventsPublication 的方法。它,例如,能夠用於潛在地清理事件列表。

 

該方法,會在Spring Data repository’s  save(…)每次被調用時執行。

2.8. Spring Data extensions (沒啥意思 略)

This section documents a set of Spring Data extensions that enable Spring Data usage in a variety of contexts. Currently most of the integration is targeted towards Spring MVC.

2.8.1. Querydsl Extension

Querydsl is a framework which enables the construction of statically typed SQL-like queries via its fluent API.

Several Spring Data modules offer integration with Querydsl via QueryDslPredicateExecutor.

Example 32. QueryDslPredicateExecutor interface
publicinterfaceQueryDslPredicateExecutor<T>{ T findOne(Predicate predicate); //(1)Iterable<T> findAll(Predicate predicate); //(2)long count(Predicate predicate); //(3)boolean exists(Predicate predicate); //(4)// … more functionality omitted.}
① 查找並返回一個可以匹配 Predicate 的entity。
② 查找並返回全部可以匹配 Predicate 的entities。
③ 返回可以匹配 Predicate 的entities的數量。
④ 返回是否存在可以匹配 Predicate 的entity。
 

To make use of Querydsl support simply extend QueryDslPredicateExecutor on your repository interface.

Example 33. Querydsl integration on repositories
interfaceUserRepositoryextendsCrudRepository<User,Long>,QueryDslPredicateExecutor<User>{}

The above enables to write typesafe queries using Querydsl Predicate s.

Predicate predicate = user.firstname.equalsIgnoreCase("dave").and(user.lastname.startsWithIgnoreCase("mathews")); userRepository.findAll(predicate);

2.8.2. Web support (沒啥意思 略)

  This section contains the documentation for the Spring Data web support as it is implemented as of Spring Data Commons in the 1.6 range. As it the newly introduced support changes quite a lot of things we kept the documentation of the former behavior in Legacy web support.

Spring Data modules ships with a variety of web support if the module supports the repository programming model. The web related stuff requires Spring MVC JARs on the classpath, some of them even provide integration with Spring HATEOAS [2]. In general, the integration support is enabled by using the @EnableSpringDataWebSupport annotation in your JavaConfig configuration class.

Example 34. Enabling Spring Data web support
@Configuration@EnableWebMvc@EnableSpringDataWebSupportclassWebConfiguration{}

The @EnableSpringDataWebSupport annotation registers a few components we will discuss in a bit. It will also detect Spring HATEOAS on the classpath and register integration components for it as well if present.

Alternatively, if you are using XML configuration, register either SpringDataWebSupport or HateoasAwareSpringDataWebSupport as Spring beans:

Example 35. Enabling Spring Data web support in XML
<beanclass="org.springframework.data.web.config.SpringDataWebConfiguration"/><!-- If you're using Spring HATEOAS as well register this one *instead* of the former --><beanclass="org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration"/>
Basic web support

The configuration setup shown above will register a few basic components:

  • A DomainClassConverter to enable Spring MVC to resolve instances of repository managed domain classes from request parameters or path variables.

  • HandlerMethodArgumentResolver implementations to let Spring MVC resolve Pageable and Sort instances from request parameters.

DomainClassConverter

The DomainClassConverter allows you to use domain types in your Spring MVC controller method signatures directly, so that you don’t have to manually lookup the instances via the repository:

Example 36. A Spring MVC controller using domain types in method signatures
@Controller@RequestMapping("/users")publicclassUserController{@RequestMapping("/{id}")publicString showUserForm(@PathVariable("id")User user,Model model){ model.addAttribute("user", user);return"userForm";}}

As you can see the method receives a User instance directly and no further lookup is necessary. The instance can be resolved by letting Spring MVC convert the path variable into the id type of the domain class first and eventually access the instance through calling findOne(…) on the repository instance registered for the domain type.

  Currently the repository has to implement CrudRepository to be eligible to be discovered for conversion.
HandlerMethodArgumentResolvers for Pageable and Sort

The configuration snippet above also registers a PageableHandlerMethodArgumentResolver as well as an instance of SortHandlerMethodArgumentResolver. The registration enables Pageable and Sort being valid controller method arguments

Example 37. Using Pageable as controller method argument
@Controller@RequestMapping("/users")publicclassUserController{@AutowiredUserRepository repository;@RequestMappingpublicString showUsers(Model model,Pageable pageable){ model.addAttribute("users", repository.findAll(pageable));return"users";}}

This method signature will cause Spring MVC try to derive a Pageable instance from the request parameters using the following default configuration:

Table 1. Request parameters evaluated for Pageable instances

page

Page you want to retrieve, 0 indexed and defaults to 0.

size

Size of the page you want to retrieve, defaults to 20.

sort

Properties that should be sorted by in the format property,property(,ASC|DESC). Default sort direction is ascending. Use multiple sort parameters if you want to switch directions, e.g. ?sort=firstname&sort=lastname,asc.

To customize this behavior extend either SpringDataWebConfiguration or the HATEOAS-enabled equivalent and override the pageableResolver() or sortResolver() methods and import your customized configuration file instead of using the @Enable-annotation.

In case you need multiple Pageable or Sort instances to be resolved from the request (for multiple tables, for example) you can use Spring’s @Qualifier annotation to distinguish one from another. The request parameters then have to be prefixed with ${qualifier}_. So for a method signature like this:

publicString showUsers(Model model,@Qualifier("foo")Pageable first,@Qualifier("bar")Pageable second){}

you have to populate foo_page and bar_page etc.

The default Pageable handed into the method is equivalent to a new PageRequest(0, 20) but can be customized using the @PageableDefaults annotation on the Pageable parameter.

Hypermedia support for Pageables

Spring HATEOAS ships with a representation model class PagedResources that allows enriching the content of a Page instance with the necessary Page metadata as well as links to let the clients easily navigate the pages. The conversion of a Page to a PagedResources is done by an implementation of the Spring HATEOAS ResourceAssembler interface, the PagedResourcesAssembler.

Example 38. Using a PagedResourcesAssembler as controller method argument
@ControllerclassPersonController{@AutowiredPersonRepository repository;@RequestMapping(value ="/persons", method =RequestMethod.GET)HttpEntity<PagedResources<Person>> persons(Pageable pageable,PagedResourcesAssembler assembler){Page<Person> persons = repository.findAll(pageable);returnnewResponseEntity<>(assembler.toResources(persons),HttpStatus.OK);}}

Enabling the configuration as shown above allows the PagedResourcesAssembler to be used as controller method argument. Calling toResources(…) on it will cause the following:

  • The content of the Page will become the content of the PagedResources instance.

  • The PagedResources will get a PageMetadata instance attached populated with information form the Page and the underlying PageRequest.

  • The PagedResources gets prev and next links attached depending on the page’s state. The links will point to the URI the method invoked is mapped to. The pagination parameters added to the method will match the setup of the PageableHandlerMethodArgumentResolver to make sure the links can be resolved later on.

Assume we have 30 Person instances in the database. You can now trigger a request GET http://localhost:8080/persons and you’ll see something similar to this:

{"links":[{"rel":"next","href":"http://localhost:8080/persons?page=1&size=20 }],"content":[// 20 Person instances rendered here],"pageMetadata":{"size":20,"totalElements":30,"totalPages":2,"number":0}}

You see that the assembler produced the correct URI and also picks up the default configuration present to resolve the parameters into a Pageable for an upcoming request. This means, if you change that configuration, the links will automatically adhere to the change. By default the assembler points to the controller method it was invoked in but that can be customized by handing in a custom Link to be used as base to build the pagination links to overloads of the PagedResourcesAssembler.toResource(…) method.

Querydsl web support

For those stores having QueryDSL integration it is possible to derive queries from the attributes contained in a Request query string.

This means that given the User object from previous samples a query string

?firstname=Dave&lastname=Matthews

can be resolved to

QUser.user.firstname.eq("Dave").and(QUser.user.lastname.eq("Matthews"))

using the QuerydslPredicateArgumentResolver.

  The feature will be automatically enabled along @EnableSpringDataWebSupport when Querydsl is found on the classpath.

Adding a @QuerydslPredicate to the method signature will provide a ready to use Predicate which can be executed via the QueryDslPredicateExecutor.

  Type information is typically resolved from the methods return type. Since those information does not necessarily match the domain type it might be a good idea to use the root attribute of QuerydslPredicate.
@ControllerclassUserController{@AutowiredUserRepository repository;@RequestMapping(value ="/", method =RequestMethod.GET)String index(Model model,@QuerydslPredicate(root =User.class)Predicate predicate, //(1)Pageable pageable,@RequestParamMultiValueMap<String,String> parameters){ model.addAttribute("users", repository.findAll(predicate, pageable));return"index";}}
Resolve query string arguments to matching   Predicate   for   User .
 

The default binding is as follows:

  • Object on simple properties as eq.

  • Object on collection like properties as contains.

  • Collection on simple properties as in.

Those bindings can be customized via the bindings attribute of @QuerydslPredicate or by making use of Java 8 default methods adding the QuerydslBinderCustomizer to the repository interface.

interfaceUserRepositoryextendsCrudRepository<User,String>,QueryDslPredicateExecutor<User>,(1)QuerydslBinderCustomizer<QUser>{(2)@Overridedefaultpublicvoid customize(QuerydslBindings bindings,QUser user){ bindings.bind(user.username).first((path, value)-> path.contains(value))(3) bindings.bind(String.class).first((StringPath path,String value)-> path.containsIgnoreCase(value));(4) bindings.excluding(user.password);(5)}}
1 QueryDslPredicateExecutor provides access to specific finder methods for Predicate.
2 QuerydslBinderCustomizer defined on the repository interface will be automatically picked up and shortcuts @QuerydslPredicate(bindings=…).
3 Define the binding for the username property to be a simple contains binding.
4 Define the default binding for String properties to be a case insensitive contains match.
5 Exclude the password property from Predicate resolution.

2.8.3. Repository populators (沒啥意思 略)

If you work with the Spring JDBC module, you probably are familiar with the support to populate a DataSource using SQL scripts. A similar abstraction is available on the repositories level, although it does not use SQL as the data definition language because it must be store-independent. Thus the populators support XML (through Spring’s OXM abstraction) and JSON (through Jackson) to define data with which to populate the repositories.

Assume you have a file data.json with the following content:

Example 39. Data defined in JSON
[{"_class":"com.acme.Person","firstname":"Dave","lastname":"Matthews"},{"_class":"com.acme.Person","firstname":"Carter","lastname":"Beauford"}]

You can easily populate your repositories by using the populator elements of the repository namespace provided in Spring Data Commons. To populate the preceding data to your PersonRepository , do the following:

Example 40. Declaring a Jackson repository populator
<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:repository="http://www.springframework.org/schema/data/repository"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository.xsd"><repository:jackson2-populatorlocations="classpath:data.json"/></beans>

This declaration causes the data.json file to be read and deserialized via a Jackson ObjectMapper.

The type to which the JSON object will be unmarshalled to will be determined by inspecting the _class attribute of the JSON document. The infrastructure will eventually select the appropriate repository to handle the object just deserialized.

To rather use XML to define the data the repositories shall be populated with, you can use the unmarshaller-populator element. You configure it to use one of the XML marshaller options Spring OXM provides you with. See the Spring 參考文檔 for details.

Example 41. Declaring an unmarshalling repository populator (using JAXB)
<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:repository="http://www.springframework.org/schema/data/repository"xmlns:oxm="http://www.springframework.org/schema/oxm"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository.xsd http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm.xsd"><repository:unmarshaller-populatorlocations="classpath:data.json"unmarshaller-ref="unmarshaller"/><oxm:jaxb2-marshallercontextPath="com.acme"/></beans>

2.8.4. Legacy web support (沒啥意思 略)

Domain class web binding for Spring MVC

Given you are developing a Spring MVC web application you typically have to resolve domain class ids from URLs. By default your task is to transform that request parameter or URL part into the domain class to hand it to layers below then or execute business logic on the entities directly. This would look something like this:

@Controller@RequestMapping("/users")publicclassUserController{privatefinalUserRepository userRepository;@AutowiredpublicUserController(UserRepository userRepository){Assert.notNull(repository,"Repository must not be null!");this.userRepository = userRepository;}@RequestMapping("/{id}")publicString showUserForm(@PathVariable("id")Long id,Model model){// Do null check for idUser user = userRepository.findOne(id);// Do null check for user model.addAttribute("user", user);return"user";}}

First you declare a repository dependency for each controller to look up the entity managed by the controller or repository respectively. Looking up the entity is boilerplate as well, as it’s always a findOne(…) call. Fortunately Spring provides means to register custom components that allow conversion between a String value to an arbitrary type.

PropertyEditors

For Spring versions before 3.0 simple Java PropertyEditors had to be used. To integrate with that, Spring Data offers a DomainClassPropertyEditorRegistrar, which looks up all Spring Data repositories registered in the ApplicationContext and registers a custom PropertyEditor for the managed domain class.

<beanclass="….web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"><propertyname="webBindingInitializer"><beanclass="….web.bind.support.ConfigurableWebBindingInitializer"><propertyname="propertyEditorRegistrars"><beanclass="org.springframework.data.repository.support.DomainClassPropertyEditorRegistrar"/></property></bean></property></bean>

If you have configured Spring MVC as in the preceding example, you can configure your controller as follows, which reduces a lot of the clutter and boilerplate.

@Controller@RequestMapping("/users")publicclassUserController{@RequestMapping("/{id}")publicString showUserForm(@PathVariable("id")User user,Model model){ model.addAttribute("user", user);return"userForm";}}

3. Query by Example

3.1.介紹

本章節會介紹 使用Example進行查詢,並解釋如何使用。

Query by Example (QBE) ,是一種用戶友好的查詢技術。它容許動態查詢建立,不須要寫包含字段名字的查詢。實際上,QBE徹底不要求寫特定存儲的查詢語言。

3.2.使用

使用Example查詢由三部分組成:

  • Probe: 一個domain object的具體example。

  • ExampleMatcherExampleMatcher 攜帶了如何匹配特定的字段的詳細信息。能夠在多個Examples之間複用。

  • Example: 由probe 和 ExampleMatcher構成的Example。被用於建立查詢。

Query by Example 適合一些用例,但仍有其侷限性:

何時使用

  • 使用一組靜態或動態限制(constraints)來查詢時。

  • 常常重構 domain objects,而不須要擔憂破壞現有查詢。

  • 獨立於底層的數據存儲API。

侷限

  • 不支持嵌套的/分組的 property constraints,如 firstname = ?0 or (firstname = ?1 and lastname = ?2)

  • 僅支持字符串的 starts/contains/ends/regex 匹配和其餘類型的精確匹配。

在開始使用 Query by Example以前,你須要有一個 domain object。先爲你的repository簡單地建立一個接口:

例子 42. 樣例 Person object
publicclassPerson{@IdprivateString id;privateString firstname;privateString lastname;privateAddress address;// … getters and setters omitted}

這是一個簡單的 domain object。你可使用它來建立一個 Example。默認,值爲null的字段會被忽略,字符串使用存儲的默認匹配。可使用Example.of() (這是一個工廠方法)建立Examples,也可使用 ExampleMatcher.。Example 是不可改變的。

例子 43. 簡單 Example
Person person =newPerson();(1) person.setFirstname("Dave");(2)Example<Person> example =Example.of(person);(3)
① 建立domain object的實例。
② 設置要查詢的properties。
③ 建立Example。

 

Examples are ideally be executed with repositories. To do so, let your repository interface extend QueryByExampleExecutor<T>. Here’s an excerpt from the QueryByExampleExecutor interface:

Example 44. The QueryByExampleExecutor
publicinterfaceQueryByExampleExecutor<T>{<S extends T> S findOne(Example<S> example);<S extends T>Iterable<S> findAll(Example<S> example);// … more functionality omitted.}

You can read more about Query by Example Execution below.

3.3. Example matchers

Examples are not limited to default settings. You can specify own defaults for string matching, null handling and property-specific settings using the ExampleMatcher.

Example 45. Example matcher with customized matching
Person person =newPerson();(1) person.setFirstname("Dave");(2)ExampleMatcher matcher =ExampleMatcher.matching()(3).withIgnorePaths("lastname")(4).withIncludeNullValues()(5).withStringMatcherEnding();(6)Example<Person> example =Example.of(person, matcher);(7)
① 建立domain object的一個實例。
② 設置properties。
③ 建立一個 ExampleMatcher   ,指望全部的values都匹配。即使沒有更多的配置,它也是可用的。
④ 構造一個新的 ExampleMatcher   ,忽略property path lastname
⑤ 構造一個新的 ExampleMatcher   ,忽略property path lastname,幷包含全部的null values
⑥ 構造一個 新的 ExampleMatcher   ,忽略property path lastname,幷包含全部的null values,並使用末尾字符串匹配。
⑦ 建立一個新的 Example   ,基於domain object和配置好的 ExampleMatcher  
 

默認,the ExampleMatcher 會指望probe裏設置的全部的 values 都匹配。你還可使用 ExampleMatcher.matchingAny().

你能夠爲單獨的properties指定行爲 (e.g. "firstname" and "lastname", "address.city" for nested properties)。你能夠調節匹配選項和大小寫敏感性。

例子 46. 配置matcher選項
ExampleMatcher matcher =ExampleMatcher.matching().withMatcher("firstname", endsWith()).withMatcher("lastname", startsWith().ignoreCase());}

另外一種方式來配置 matcher選項,是使用 Java 8 lambdas。這種一種回調,要求實現者修改matcher。不要求返回matcher,由於配置選項是在matcher實例中保持的。

例子 47. 使用lambdas來配置 matcher選項
ExampleMatcher matcher =ExampleMatcher.matching().withMatcher("firstname", match -> match.endsWith()).withMatcher("firstname", match -> match.startsWith());}

使用Example 建立的查詢,使用了一種融合的配置view。默認的匹配設置,能夠在 ExampleMatcher 級別上設置,同時單個的設置也能夠應用到特定的property paths上。設置值 ExampleMatcher 上的設置,會被property path settings繼承,除非它們( property path settings)有顯式地定義-- 這樣有更高的優先級。

Table 2. Scope of ExampleMatcher settings
Setting Scope

Null-handling

ExampleMatcher

String matching

ExampleMatcher and property path

Ignoring properties

Property path

Case sensitivity

ExampleMatcher and property path

Value transformation

Property path

4. Auditing 審計

4.1. 基礎

Spring Data提供了複雜的支持,以透明地跟蹤誰建立或更改了entity,以及發生的時間。爲了利用這種功能,你須要爲你的entity classes裝備上auditing metadata -- 可使用註解,或者經過實現特定的接口。

4.1.1. 基於註解的auditing metadata

咱們提供了 @CreatedBy@LastModifiedBy 來捕獲建立或修改entity的用戶,還提供了 @CreatedDate@LastModifiedDate 來捕獲發生的時間。

例子 48. 一個被審計的entity
classCustomer{@CreatedByprivateUser user;@CreatedDateprivateDateTime createdDate;// … further properties omitted}

如你所見,能夠有選擇地適用註解,取決於你想捕獲哪些信息。捕獲時間的註解,能夠用於properties of type JodaTimes DateTime、傳統的 Java DateCalendar、JDK8 date/time types,以及 long/Long

4.1.2. 基於接口的auditing metadata

若是你不想使用註解來定義auditing metadata,你可讓你的domain class實現Auditable 接口。它暴露了全部auditing properties的setter methods。

還有一個便捷的基類 AbstractAuditable 能夠繼承,以免須要手動實現接口方法。注意,這增長了與Spring的耦合,這是咱們極力避免的。一般推薦使用註解方式,由於更少侵入,更加彈性。

4.1.3. AuditorAware

當你使用 @CreatedBy@LastModifiedBy時,the auditing infrastructure須要知道當前的 principal。咱們提供了一個 AuditorAware<T> SPI 接口,你必須實現,以告訴the infrastructure當前用戶或者系統是什麼。泛型 T 定義了什麼類型的 properties註解了 @CreatedBy@LastModifiedBy

這裏是一個示例實現了該接口,使用Spring Security的Authentication  object:

例子 49. 基於Spring Security的 AuditorAware實現
classSpringSecurityAuditorAwareimplementsAuditorAware<User>{publicUser getCurrentAuditor(){Authentication authentication =SecurityContextHolder.getContext().getAuthentication();if(authentication ==null||!authentication.isAuthenticated()){returnnull;}return((MyUserDetails) authentication.getPrincipal()).getUser();}}

 

附錄

附錄 A: Namespace reference

The <repositories /> element

The <repositories /> element triggers the setup of the Spring Data repository infrastructure. The most important attribute is base-package which defines the package to scan for Spring Data repository interfaces.[3]

Table 3. Attributes
Name Description

base-package

Defines the package to be used to be scanned for repository interfaces extending *Repository (actual interface is determined by specific Spring Data module) in auto detection mode. All packages below the configured package will be scanned, too. Wildcards are allowed.

repository-impl-postfix

Defines the postfix to autodetect custom repository implementations. Classes whose names end with the configured postfix will be considered as candidates. Defaults to Impl.

query-lookup-strategy

Determines the strategy to be used to create finder queries. See Query lookup strategies for details. Defaults to create-if-not-found.

named-queries-location

Defines the location to look for a Properties file containing externally defined queries.

consider-nested-repositories

Controls whether nested repository interface definitions should be considered. Defaults to false.

附錄 B: Populators namespace reference

The <populator /> element

The <populator /> element allows to populate the a data store via the Spring Data repository infrastructure.[4]

Table 4. Attributes
Name Description

locations

Where to find the files to read the objects from the repository shall be populated with.

附錄 C: Repository查詢關鍵字

支持的查詢關鍵字

The following table lists the keywords generally supported by the Spring Data repository query derivation mechanism. However, consult the store-specific documentation for the exact list of supported keywords, because some listed here might not be supported in a particular store.

Table 5. Query keywords
Logical keyword Keyword expressions

AND

And

OR

Or

AFTER

After, IsAfter

BEFORE

Before, IsBefore

CONTAINING

Containing, IsContaining, Contains

BETWEEN

Between, IsBetween

ENDING_WITH

EndingWith, IsEndingWith, EndsWith

EXISTS

Exists

FALSE

False, IsFalse

GREATER_THAN

GreaterThan, IsGreaterThan

GREATER_THAN_EQUALS

GreaterThanEqual, IsGreaterThanEqual

IN

In, IsIn

IS

Is, Equals, (or no keyword)

IS_NOT_NULL

NotNull, IsNotNull

IS_NULL

Null, IsNull

LESS_THAN

LessThan, IsLessThan

LESS_THAN_EQUAL

LessThanEqual, IsLessThanEqual

LIKE

Like, IsLike

NEAR

Near, IsNear

NOT

Not, IsNot

NOT_IN

NotIn, IsNotIn

NOT_LIKE

NotLike, IsNotLike

REGEX

Regex, MatchesRegex, Matches

STARTING_WITH

StartingWith, IsStartingWith, StartsWith

TRUE

True, IsTrue

WITHIN

Within, IsWithin

附錄 D: Repository查詢的返回類型

支持的查詢的返回類型

The following table lists the return types generally supported by Spring Data repositories. However, consult the store-specific documentation for the exact list of supported return types, because some listed here might not be supported in a particular store.

  Geospatial types like (GeoResult, GeoResults, GeoPage) are only available for data stores that support geospatial queries.
Table 6. Query return types
Return type Description

void

Denotes no return value.

Primitives

Java primitives.

Wrapper types

Java wrapper types.

T

An unique entity. Expects the query method to return one result at most. In case no result is found null is returned. More than one result will trigger an IncorrectResultSizeDataAccessException.

Iterator<T>

An Iterator.

Collection<T>

A Collection.

List<T>

A List.

Optional<T>

A Java 8 or Guava Optional. Expects the query method to return one result at most. In case no result is found Optional.empty()/Optional.absent() is returned. More than one result will trigger an IncorrectResultSizeDataAccessException.

Option<T>

An either Scala or JavaSlang Option type. Semantically same behavior as Java 8’s Optional described above.

Stream<T>

A Java 8 Stream.

Future<T>

A Future. Expects method to be annotated with @Async and requires Spring’s asynchronous method execution capability enabled.

CompletableFuture<T>

A Java 8 CompletableFuture. Expects method to be annotated with @Async and requires Spring’s asynchronous method execution capability enabled.

ListenableFuture

A org.springframework.util.concurrent.ListenableFuture. Expects method to be annotated with @Async and requires Spring’s asynchronous method execution capability enabled.

Slice

A sized chunk of data with information whether there is more data available. Requires a Pageable method parameter.

Page<T>

A Slice with additional information, e.g. the total number of results. Requires a Pageable method parameter.

GeoResult<T>

A result entry with additional information, e.g. distance to a reference location.

GeoResults<T>

A list of GeoResult<T> with additional information, e.g. average distance to a reference location.

GeoPage<T>

A Page with GeoResult<T>, e.g. average distance to a reference location.

相關文章
相關標籤/搜索