爲知筆記版本在這裏,帶格式。javascript
前言
參考文檔
1. 依賴
因爲Spring Data modules不一樣的開發時間,因此它們大多數都有不一樣的主版本號和小版本號。想要找到兼容版本的最佳方式是依賴Spring Data Release Train BOM,已經提供了兼容版本定義。在Maven項目中,你能夠在POM的 <dependencyManagement />部分聲明該依賴:html
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-releasetrain</artifactId>
<version>${release-train}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
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
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
</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功能。
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.}
JpaRepository
or
MongoRepository
。這些接口繼承自
CrudRepository
,並暴露出底層持久化技術的能力,而不只僅是泛泛的持久化技術接口(如
CrudRepository
)的能力。
基於CrudRepository
還有一個 PagingAndSortingRepository
abstraction,添加了分頁相關的功能:
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查詢的衍生:
publicinterfaceUserRepositoryextendsCrudRepository<User,Long>{Long countByLastname(String lastname);}
publicinterfaceUserRepositoryextendsCrudRepository<User,Long>{Long deleteByLastname(String lastname);List<User> removeByLastname(String lastname);}
2.2. Query methods 查詢方法
標準的CRUD functionality repositories,一般會查詢底層數據存儲。使用Spring Data,只須要如下四個步驟:
-
聲明一個接口,繼承Repository 接口或其子接口,填上entity類型和id類型(主鍵類型)。
interfacePersonRepositoryextendsRepository<Person,Long>{…}
-
在該接口中聲明查詢方法。
interfacePersonRepositoryextendsRepository<Person,Long>{List<Person> findByLastname(String lastname);}
-
設置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屬性。
-
獲取注入的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接口會繼承 Repository
、 CrudRepository
或者 PagingAndSortingRepository
。或者,若是你不想繼承Spring Data interfaces的話,你能夠在你的repository接口上面註解 @RepositoryDefinition
。 繼承 CrudRepository
會暴露一個完整的方法集合,能夠操做你的entities。若是你只想有選擇地暴露一些方法,能夠從 CrudRepository
中複製到你的domain repository。
這容許你定義你本身的abstraction,基於Spring Data Repositories的功能。
@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(…)
。
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定義:
-
若是該repository定義 繼承自特定模塊的repository( extends the module-specific repository),那麼它就是特定Spring Data module的一個有效的候選。
-
若是該domain class 被註解了特定模塊的類註解( annotated with the module-specific type annotation),那麼它就是特定Spring Data module的一個有效的候選。Spring Data modules接受第三方註解 (如 JPA的
@Entity
),或者提供本身的註解如針對 Spring Data MongoDB/Spring Data Elasticsearch的@Document
註解。
interfaceMyRepositoryextendsJpaRepository<User,Long>{}@NoRepositoryBeaninterfaceMyBaseRepository<T, ID extendsSerializable>extendsJpaRepository<T, ID>{…}interfaceUserRepositoryextendsMyBaseRepository<User,Long>{…}
MyRepository
和UserRepository
繼承自JpaRepository
。他們都是Spring Data JPA module的有效的候選。
interfaceAmbiguousRepositoryextendsRepository<User,Long>{…}@NoRepositoryBeaninterfaceMyBaseRepository<T, ID extendsSerializable>extendsCrudRepository<T, ID>{…}interfaceAmbiguousUserRepositoryextendsMyBaseRepository<User,Long>{…}
AmbiguousRepository
和AmbiguousUserRepository
分別繼承自 Repository
和 CrudRepository
。單獨使用時沒有問題,但多個模塊並存時,就沒法區分repositories該使用哪一個模塊。
interfacePersonRepositoryextendsRepository<Person,Long>{…}@EntitypublicclassPerson{…}interfaceUserRepositoryextendsRepository<User,Long>{…}@DocumentpublicclassUser{…}
PersonRepository
引用了帶有JPA註解 @Entity
的 Person
,所以,該repository明顯屬於 Spring Data JPA。 UserRepository
使用了帶有 Spring Data MongoDB’s @Document
註解的 User。
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 details 和 identifying 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 是強制填寫的。
@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
(默認):結合了CREATE
和USE_DECLARED_QUERY
。
2.4.2. Query 建立
Spring Data repository infrastructure中內置的query builder機制,在repository的entities上構建限制查詢(constraining queries )時頗有用。該機制從方法名中脫去了前綴 find…By
、 read…By
、 query…By
、 count…By
、 以及 get…By
,並解析剩餘的部分。引入的語句,能夠包含更多表達式,如 Distinct
。然而,第一個 By
扮演了分隔符角色,標明瞭實際criteria 的開始。在一個很是基礎的級別上,你能夠定義一些條件,並使用 And
和 Or
來鏈接它們。
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,結合操做符(可能鏈接到一塊兒)。你可使用
AND
和OR
拼接。你也可使用諸如Between
、LessThan
、GreaterThan
、Like
之類的操做符。不一樣的數據存儲可能支持不一樣的操做符,因此,最好查閱相關參考文檔。 -
方法解析器支持
IgnoreCase
flag,能夠爲單獨的 properties設置 (例如,findByLastnameIgnoreCase(…)
),也能夠爲全部的 properties設置 (通常是String
instances,例如,findByLastnameAndFirstnameAllIgnoreCase(…)
)。是否支持忽略大小寫,不一樣的存儲可能不一樣的結果,因此,請查閱相關的參考文檔。 -
你可使用靜態排序,只須要在方法名上追加一個
OrderBy
語句,須要引用一個property,還要提供排序方式 (Asc
或Desc
)。若是想建立支持動態排序的查詢,見 特殊參數處理。
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
,以動態的應用分頁和排序。
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. 限制查詢結果
查詢方法的結果可使用關鍵字 first
或 top
來限制,這兩個能夠互換。一個可選的數值能夠追加到 top/first ,以標明最大返回的數量。若是留空,默認是1。
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>
做爲返回返回類型,查詢方法的結果能夠被加速處理。
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<T>
result
try(Stream<User> stream = repository.findAllByCustomQueryAndStream()){ stream.forEach(…);}
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)
2.5. 建立 repository instances
在本部分,你會建立 定義過的repository接口的實例和bean 定義。一種方式是使用 Spring namespace,但咱們通常都推薦使用Java-Config 形式的配置。
2.5.1. XML configuration
每一個 Spring Data module 都包含一個 repositories element,容許你簡單地定義一個base package -- Spring會掃描它。
<?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
接口,可能被註冊成 userRepository
。base-package
attribute 容許通配符,因此,你還能夠定義一個pattern。
使用過濾器
默認,the infrastructure會撿起每個繼承自持久化技術特定的 Repository
及其子接口的接口 -- 要在base package之下,並建立一個bean實例。可是,你或許想要更細化的控制。這時,你須要使用 <repositories /> 中的 <include-filter />
和 <exclude-filter />
elements。詳見 Spring 參考文檔 。
例如,想要排除特定接口,你可能會使用下面的配置:
<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
註解來開啓。
下面是一個簡單的例子。
@Configuration@EnableJpaRepositories("com.acme.repositories")classApplicationConfiguration{@BeanpublicEntityManagerFactory entityManagerFactory(){// …}}
EntityManagerFactory
bean。
2.5.3. 單獨使用 Standalone usage
你能夠在Spring container以外使用 the repository infrastructure,例如,在CDI 環境中。你仍然須要一些Spring lib,但通常,你也能夠經過編碼設置repositories。
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 接口來繼承這個自定義的接口。
interfaceUserRepositoryCustom{publicvoid someCustomMethod(User user);}
classUserRepositoryImplimplementsUserRepositoryCustom{publicvoid someCustomMethod(User user){// Your custom implementation}}
Impl
。
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
。
<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.
<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.
@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.
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:
@Configuration@EnableJpaRepositories(repositoryBaseClass =MyRepositoryImpl.class)classApplicationConfiguration{…}
A corresponding attribute is available in the XML namespace.
<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
,你能夠用於一個方法上,從而使得發佈事件足夠簡單。
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
.
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.}
To make use of Querydsl support simply extend QueryDslPredicateExecutor
on your repository interface.
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.
@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:
<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:
@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
@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:
|
Page you want to retrieve, 0 indexed and defaults to 0. |
|
Size of the page you want to retrieve, defaults to 20. |
|
Properties that should be sorted by in the format |
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
.
@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 thePagedResources
instance. -
The
PagedResources
will get aPageMetadata
instance attached populated with information form thePage
and the underlyingPageRequest
. -
The
PagedResources
getsprev
andnext
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 thePageableHandlerMethodArgumentResolver
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";}}
Predicate
for
User
.
The default binding is as follows:
-
Object
on simple properties aseq
. -
Object
on collection like properties ascontains
. -
Collection
on simple properties asin
.
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:
[{"_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:
<?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.
<?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。
-
ExampleMatcher
:ExampleMatcher
攜帶了如何匹配特定的字段的詳細信息。能夠在多個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簡單地建立一個接口:
publicclassPerson{@IdprivateString id;privateString firstname;privateString lastname;privateAddress address;// … getters and setters omitted}
這是一個簡單的 domain object。你可使用它來建立一個 Example
。默認,值爲null的字段會被忽略,字符串使用存儲的默認匹配。可使用Example.of() (這是一個工廠方法)建立Examples,也可使用 ExampleMatcher
.。Example
是不可改變的。
Person person =newPerson();(1) person.setFirstname("Dave");(2)Example<Person> example =Example.of(person);(3)
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:
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
.
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)
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)。你能夠調節匹配選項和大小寫敏感性。
ExampleMatcher matcher =ExampleMatcher.matching().withMatcher("firstname", endsWith()).withMatcher("lastname", startsWith().ignoreCase());}
另外一種方式來配置 matcher選項,是使用 Java 8 lambdas。這種一種回調,要求實現者修改matcher。不要求返回matcher,由於配置選項是在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)有顯式地定義-- 這樣有更高的優先級。
Setting | Scope |
---|---|
Null-handling |
|
String matching |
|
Ignoring properties |
Property path |
Case sensitivity |
|
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
來捕獲發生的時間。
classCustomer{@CreatedByprivateUser user;@CreatedDateprivateDateTime createdDate;// … further properties omitted}
如你所見,能夠有選擇地適用註解,取決於你想捕獲哪些信息。捕獲時間的註解,能夠用於properties of type JodaTimes DateTime
、傳統的 Java Date
和 Calendar
、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:
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]
Name | Description |
---|---|
|
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. |
|
Defines the postfix to autodetect custom repository implementations. Classes whose names end with the configured postfix will be considered as candidates. Defaults to |
|
Determines the strategy to be used to create finder queries. See Query lookup strategies for details. Defaults to |
|
Defines the location to look for a Properties file containing externally defined queries. |
|
Controls whether nested repository interface definitions should be considered. Defaults to |
附錄 B: Populators namespace reference
The <populator /> element
The <populator />
element allows to populate the a data store via the Spring Data repository infrastructure.[4]
Name | Description |
---|---|
|
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.
Logical keyword | Keyword expressions |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
附錄 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. |
Return type | Description |
---|---|
|
Denotes no return value. |
Primitives |
Java primitives. |
Wrapper types |
Java wrapper types. |
|
An unique entity. Expects the query method to return one result at most. In case no result is found |
|
An |
|
A |
|
A |
|
A Java 8 or Guava |
|
An either Scala or JavaSlang |
|
A Java 8 |
|
A |
|
A Java 8 |
|
A |
|
A sized chunk of data with information whether there is more data available. Requires a |
|
A |
|
A result entry with additional information, e.g. distance to a reference location. |
|
A list of |
|
A |