©2008-2016原做者。javascript
本文件副本可供您自行使用並分發給其餘人,前提是您不收取任何此類副本的費用,並進一步規定每份副本均包含此版權聲明,不管是以印刷版仍是電子版分發。 |
前言
項目元數據
在使用Spring Data JPA時,您可能會發現如下連接有幫助:html
-
Bugtracker:https://jira.spring.io/browse/DATAJPAgit
-
發佈存儲庫:https : //repo.spring.io/libs-releasegithub
-
里程碑存儲庫:https : //repo.spring.io/libs-milestoneweb
-
快照存儲庫:https : //repo.spring.io/libs-snapshot正則表達式
1.新的&值得注意的
1.1。Spring Data JPA 1.11中的新特性
Spring Data JPA 1.11增長了如下功能:算法
-
改進與Hibernate 5.2的兼容性。spring
-
經過示例支持任意匹配模式。sql
-
分頁查詢執行優化。
-
支持
exists
存儲庫查詢派生中的投影。
1.2。Spring Data JPA 1.10中的新特性
Spring Data JPA 1.10增長了如下功能:
因爲各個Spring Data模塊的初始日期不一樣,它們中的大多數都帶有不一樣的主版本號和次版本號。尋找兼容版本的最簡單方法是依靠咱們隨定義的兼容版本提供的Spring Data Release BOM。在Maven項目中,您將在<dependencyManagement />
POM 的部分聲明這種依賴關係,以下所示:
<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>
目前的發行版本是Kay-SR7
。列車名稱按字母順序上升,目前可用的列車在此處列出。版本名稱遵循如下模式:${name}-${release}
,其中發佈能夠是下列之一:
-
BUILD-SNAPSHOT
:當前快照 -
M1
,M2
等等:里程碑 -
RC1
,RC2
等等:發佈候選人 -
RELEASE
:GA版本 -
SR1
,SR2
等等:服務版本
在咱們的Spring Data示例存儲庫中能夠找到使用BOM的一個工做示例。有了這個,你能夠在塊中聲明你想使用的Spring數據模塊而不須要版本<dependencies />
,以下所示:
<dependencies> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> </dependency> <dependencies>
1.3。Spring Boot的依賴管理
Spring Boot爲您選擇最新版本的Spring Data模塊。若是您仍想升級到較新版本,請將該屬性配置爲您要使用spring-data-releasetrain.version
的火車名稱和迭代。
1.4。Spring框架
當前版本的Spring Data模塊須要版本5.0.6.RELEASE或更高版本的Spring Framework。這些模塊也可能與該次要版本的舊版錯誤修復版本一塊兒工做。可是,強烈建議使用該代中的最新版本。:彈簧框架,文檔:http://docs.spring.io/spring/docs/5.0.6.RELEASE/spring-framework-reference :彈簧骨架的javadoc:https://docs.spring.io/spring /docs/5.0.6.RELEASE/javadoc-api
2.使用Spring數據存儲庫
Spring Data存儲庫抽象的目標是顯着減小爲各類持久性存儲實現數據訪問層所需的樣板代碼數量。
Spring數據存儲庫文檔和你的模塊 |
2.1。核心概念
Spring Data存儲庫抽象中的中心接口是Repository
。它須要管理域類以及域類的ID類型做爲類型參數。該接口主要做爲標記接口來捕獲要使用的類型,並幫助您發現擴展該接口的接口。該CrudRepository
規定對於正在管理的實體類複雜的CRUD功能。
CrudRepository
接口
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> { <S extends T> S save(S entity); Optional<T> findById(ID primaryKey); Iterable<T> findAll(); long count(); void delete(T entity); boolean existsById(ID primaryKey); // … more functionality omitted. }
保存給定的實體。 | |
返回由給定ID標識的實體。 | |
返回全部實體。 | |
返回實體的數量。 | |
刪除給定的實體。 | |
指示是否存在具備給定ID的實體。 |
咱們還提供持久性技術特定抽象,如JpaRepository 或MongoRepository 。這些接口擴展CrudRepository 並公開了持久化技術的基本功能,以及至關通用的持久化技術無關接口,例如CrudRepository 。 |
除此以外CrudRepository
,還有一個PagingAndSortingRepository
抽象增長了其餘方法來簡化對實體的分頁訪問:
PagingAndSortingRepository
接口
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> { Iterable<T> findAll(Sort sort); Page<T> findAll(Pageable pageable); }
要訪問User
頁面大小爲20 的第二頁,您能夠執行如下操做:
PagingAndSortingRepository<User, Long> repository = // … get access to a bean Page<User> users = repository.findAll(new PageRequest(1, 20));
除了查詢方法外,count和delete查詢的查詢派生都是可用的。如下列表顯示派生計數查詢的接口定義:
interface UserRepository extends CrudRepository<User, Long> { long countByLastname(String lastname); }
如下列表顯示派生刪除查詢的接口定義:
interface UserRepository extends CrudRepository<User, Long> { long deleteByLastname(String lastname); List<User> removeByLastname(String lastname); }
2.2。查詢方法
標準CRUD功能存儲庫一般會在底層數據存儲上進行查詢。使用Spring Data,聲明這些查詢變成了一個四步過程:
-
聲明一個擴展Repository或其子接口的接口,並將其鍵入它應該處理的域類和ID類型,如如下示例所示:
interface PersonRepository extends Repository<Person, Long> { … }
-
在接口上聲明查詢方法。
interface PersonRepository extends Repository<Person, Long> { List<Person> findByLastname(String lastname); }
-
設置Spring以使用JavaConfig或XML配置爲這些接口建立代理實例。
-
要使用Java配置,請建立相似於如下的類:
import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @EnableJpaRepositories class Config {}
-
要使用XML配置,請定義一個相似於如下的bean:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="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:repositories base-package="com.acme.repositories"/> </beans>
這個例子中使用了JPA命名空間。若是您對任何其餘商店使用存儲庫抽象,則須要將其更改成商店模塊的相應名稱空間聲明。換句話說,你應該交換
jpa
同意,例如mongodb
。+另請注意,JavaConfig變體不會顯式配置包,由於缺省狀況下會使用註釋類的包。要定製要掃描的軟件包,請使用
basePackage…
特定於數據存儲庫的@Enable${store}Repositories
註釋的一個屬性。 -
-
注入資源庫實例並使用它,如如下示例所示:
class SomeClient { private final PersonRepository repository; SomeClient(PersonRepository repository) { this.repository = repository; } void doSomething() { List<Person> persons = repository.findByLastname("Matthews"); } }
如下部分詳細解釋每一步:
2.3。定義存儲庫接口
首先,定義一個域類特定的存儲庫接口。該接口必須擴展Repository
並鍵入域類和ID類型。若是您想公開該域類型的CRUD方法,請擴展CrudRepository
而不是Repository
。
2.3.1。微調儲存庫定義
一般狀況下,你的資料庫接口擴展Repository
,CrudRepository
或PagingAndSortingRepository
。或者,若是您不想擴展Spring Data接口,也可使用註釋庫接口進行註釋@RepositoryDefinition
。擴展CrudRepository
公開了一套完整的方法來操縱你的實體。若是您想選擇暴露的方法,請將要公開的方法複製CrudRepository
到您的域存儲庫中。
這樣作可讓您在提供的Spring Data Repositories功能之上定義本身的抽象。 |
如下示例顯示如何選擇性地公開CRUD方法(findById
以及save
在這種狀況下):
@NoRepositoryBean interface MyBaseRepository<T, ID extends Serializable> extends Repository<T, ID> { Optional<T> findById(ID id); <S extends T> S save(S entity); } interface UserRepository extends MyBaseRepository<User, Long> { User findByEmailAddress(EmailAddress emailAddress); }
在前面的例子,你定義爲全部站點庫一個共同的基礎界面和暴露findById(…)
,以及save(…)
。這些方法被髮送到基礎信息庫實現你所選擇的由Spring提供的數據(例如,若是使用JPA商店,該實現是SimpleJpaRepository
),由於它們匹配中的方法簽名CrudRepository
。所以,UserRepository
如今能夠保存用戶,經過ID查找單個用戶,並觸發查詢以Users
經過電子郵件地址查找。
中間存儲庫接口用註釋@NoRepositoryBean 。確保將該註釋添加到Spring Data不該在運行時爲其建立實例的全部存儲庫接口。 |
2.3.2。倉庫方法的空處理
從Spring Data 2.0開始,返回單個聚合實例的存儲庫CRUD方法使用Java 8 Optional
來指示潛在的缺失值。除此以外,Spring Data支持在查詢方法上返回如下包裝類型:
-
com.google.common.base.Optional
-
scala.Option
-
io.vavr.control.Option
-
javaslang.control.Option
(因爲Javaslang已被棄用)
或者,查詢方法能夠選擇不使用包裝類型。而後經過返回來指示沒有查詢結果null
。存儲庫方法返回集合,集合備選方案,包裝器和流保證永遠不會返回null
,而是相應的空表示。有關詳細信息,請參閱「 存儲庫查詢返回類型 」。
可空性註釋
您可使用Spring Framework的可空性註釋來表示存儲庫方法的可空約束。它們提供了一個工具友好的方法,並null
在運行期間進行選擇性檢查,以下所示:
-
{spring-framework-javadoc} /org/springframework/lang/NonNullApi.html [
@NonNullApi
]:用於包級別來聲明參數和返回值的默認行爲是不接受或生成null
值。 -
{spring-framework-javadoc} /org/springframework/lang/NonNull.html [
@NonNull
]:用於參數或返回值,不能是null
(不須要參數,返回值@NonNullApi
適用)。 -
{spring-framework-javadoc} /org/springframework/lang/Nullable.html [
@Nullable
]:用於能夠是參數或返回值null
。
Spring註釋使用JSR 305註釋進行元註釋(一種休眠但普遍傳播的JSR)。JSR 305元註釋讓工具供應商(如IDEA,Eclipse和Kotlin)以通用方式提供空安全支持,而無需爲Spring註釋提供硬編碼支持。要啓用對查詢方法的可空約束的運行時檢查,您須要使用Spring的@NonNullApi
in 激活包級別的非可空性package-info.java
,如如下示例所示:
package-info.java
@org.springframework.lang.NonNullApi package com.acme;
一旦存在非空默認值,存儲庫查詢方法調用就會在運行時驗證可空性約束。若是查詢執行結果違反了定義的約束,則會引起異常。當方法返回時,會發生這種狀況,null
但聲明爲非空(存儲庫所在的包上定義了註釋的默認值)。若是您想再次選擇可空結果,請選擇性地使用@Nullable
單個方法。使用本節開始提到的結果包裝類型將繼續按預期工做:將空結果轉換爲表示不存在的值。
如下示例顯示了剛剛描述的許多技術:
package com.acme; import org.springframework.lang.Nullable; interface UserRepository extends Repository<User, Long> { User getByEmailAddress(EmailAddress emailAddress); @Nullable User findByEmailAddress(@Nullable EmailAddress emailAdress); Optional<User> findOptionalByEmailAddress(EmailAddress emailAddress); }
存儲庫駐留在咱們爲其定義非空行爲的包(或子包)中。 | |
EmptyResultDataAccessException 當執行的查詢不產生結果時拋出一個。IllegalArgumentException 當emailAddress 交給方法時拋出一個null 。 |
|
null 當執行的查詢不會產生結果時返回。也接受null 做爲的價值emailAddress 。 |
|
Optional.empty() 當執行的查詢不會產生結果時返回。IllegalArgumentException 當emailAddress 交給方法時拋出一個null 。 |
基於Kotlin的知識庫中的可空性
Kotlin定義了可用於語言的可空性約束。Kotlin代碼編譯爲字節碼,該字節碼不經過方法簽名表示可空約束,而是經過編譯後的元數據表示。確保kotlin-reflect
在項目中包含JAR,以便對Kotlin的可空性限制進行檢討。Spring Data存儲庫使用語言機制來定義這些約束以應用相同的運行時檢查,以下所示:
interface UserRepository : Repository<User, String> { fun findByUsername(username: String): User fun findByFirstname(firstname: String?): User? }
該方法將參數和結果定義爲不可空(Kotlin默認值)。Kotlin編譯器拒絕傳遞null 給方法的方法調用。若是查詢執行產生空結果,EmptyResultDataAccessException 則拋出。 |
|
該方法接受null 的firstname 參數,並返回null ,若是查詢執行不產生結果。 |
2.3.3。將存儲庫與多個Spring數據模塊一塊兒使用
在應用程序中使用獨特的Spring Data模塊使事情變得簡單,由於定義範圍中的全部存儲庫接口都綁定到Spring Data模塊。有時,應用程序須要使用多個Spring Data模塊。在這種狀況下,存儲庫定義必須區分持久性技術。當它在類路徑中檢測到多個存儲庫工廠時,Spring Data會進入嚴格的存儲庫配置模式。嚴格配置使用存儲庫或域類上的詳細信息來決定存儲庫定義的Spring Data模塊綁定:
-
若是存儲庫定義擴展了特定於模塊的存儲庫,那麼它是特定Spring Data模塊的有效候選者。
-
若是域類使用特定於模塊的類型註釋進行註釋,則它是特定的Spring Data模塊的有效候選者。Spring Data模塊接受第三方註釋(好比JPA
@Entity
)或者提供他們本身的註解(例如@Document
Spring Data MongoDB和Spring Data Elasticsearch)。
如下示例顯示使用模塊特定接口(本例中爲JPA)的存儲庫:
interface MyRepository extends JpaRepository<User, Long> { } @NoRepositoryBean interface MyBaseRepository<T, ID extends Serializable> extends JpaRepository<T, ID> { … } interface UserRepository extends MyBaseRepository<User, Long> { … }
MyRepository
並在其類型層次中進行UserRepository
擴展JpaRepository
。它們是Spring Data JPA模塊的有效候選者。
如下示例顯示使用通用接口的存儲庫:
interface AmbiguousRepository extends Repository<User, Long> { … } @NoRepositoryBean interface MyBaseRepository<T, ID extends Serializable> extends CrudRepository<T, ID> { … } interface AmbiguousUserRepository extends MyBaseRepository<User, Long> { … }
AmbiguousRepository
和AmbiguousUserRepository
僅延伸Repository
,並CrudRepository
在他們的類型層次。雖然在使用獨特的Spring Data模塊時這很是好,但多個模塊沒法區分這些存儲庫應綁定到哪一個特定的Spring Data。
如下示例顯示了使用帶註釋的域類的存儲庫:
interface PersonRepository extends Repository<Person, Long> { … } @Entity class Person { … } interface UserRepository extends Repository<User, Long> { … } @Document class User { … }
PersonRepository
引用Person
,它是用JPA @Entity
註解註釋的,因此這個存儲庫顯然屬於Spring Data JPA。UserRepository
引用User
,它是用Spring Data MongoDB的@Document
註解註釋的。
如下錯誤示例顯示了使用具備混合註釋的域類的存儲庫:
interface JpaPersonRepository extends Repository<Person, Long> { … } interface MongoDBPersonRepository extends Repository<Person, Long> { … } @Entity @Document class Person { … }
此示例顯示了使用JPA和Spring Data MongoDB註釋的域類。它定義了兩個存儲庫,JpaPersonRepository
而且MongoDBPersonRepository
。一個用於JPA,另外一個用於MongoDB的使用。Spring Data再也不可以區分存儲庫,致使未定義的行爲。
存儲庫類型細節和區分域類註釋用於嚴格的存儲庫配置,以識別特定的Spring Data模塊的存儲庫候選項。能夠在相同的域類型上使用多個持久性技術特定的註釋,而且容許跨多個持久性技術重用域類型。可是,Spring Data再也不能夠肯定綁定存儲庫的惟一模塊。
區分存儲庫的最後一種方法是經過肯定存儲庫基礎包的範圍。基本軟件包定義了掃描存儲庫接口定義的起點,這意味着存儲庫定義位於相應的軟件包中。默認狀況下,註釋驅動的配置使用配置類的包。基於XML的配置中的基礎包是強制性的。
如下示例顯示基本包的註釋驅動配置:
@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa") @EnableMongoRepositories(basePackages = "com.acme.repositories.mongo") interface Configuration { }
2.4。定義查詢方法
存儲庫代理有兩種方法能夠從方法名稱派生特定於存儲的查詢:
-
經過直接從方法名稱派生查詢。
-
經過使用手動定義的查詢。
可用選項取決於實際商店。可是,必須有一個策略來決定建立什麼實際查詢。下一節介紹可用的選項。
2.4.1。查詢查詢策略
如下策略可用於存儲庫基礎結構來解析查詢。使用XML配置,您能夠經過query-lookup-strategy
屬性在名稱空間配置策略。對於Java配置,您可使用註釋的queryLookupStrategy
屬性Enable${store}Repositories
。某些策略可能不支持特定的數據存儲。
-
CREATE
嘗試從查詢方法名稱構造特定於商店的查詢。通常的方法是從方法名稱中移除一組已知的前綴,並解析該方法的其他部分。您能夠在「 查詢建立 」中閱讀有關查詢構建的更多信息。 -
USE_DECLARED_QUERY
嘗試查找已聲明的查詢,並在找不到某個查詢時引起異常。查詢能夠經過某處的註釋或其餘方式聲明。查閱特定商店的文檔以查找該商店的可用選項。若是存儲庫基礎結構在引導時未找到該方法的已聲明查詢,則會失敗。 -
CREATE_IF_NOT_FOUND
(默認)組合CREATE
和USE_DECLARED_QUERY
。它首先查找已聲明的查詢,而且若是未找到已聲明的查詢,則會建立一個自定義的基於方法名稱的查詢。這是默認的查找策略,所以,若是您沒有明確配置任何內容,就會使用它。它容許經過方法名稱進行快速查詢定義,還能夠根據須要引入已聲明的查詢來自定義這些查詢。
2.4.2。查詢建立
構建到Spring Data存儲庫基礎結構中的查詢構建器機制對構建存儲庫實體上的約束查詢很是有用。該機制條前綴find…By
,read…By
,query…By
,count…By
,和get…By
從所述方法和開始分析它的其他部分。引入子句能夠包含更多的表達式,例如Distinct
在要建立的查詢上設置不一樣的標誌。可是,第一個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
,和Like
該屬性的表達式。受支持的操做員可能因數據存儲而異,所以請參閱相應部分的參考文檔。 -
方法解析器支持
IgnoreCase
爲單個屬性(例如,findByLastnameIgnoreCase(…)
)或支持忽略大小寫的類型的全部屬性(一般爲String
實例 - 例如findByLastnameAndFirstnameAllIgnoreCase(…)
)設置標誌。支持忽略狀況的方式可能因商店而異,所以請參閱參考文檔中的相關部分以獲取特定於商店的查詢方法。 -
您能夠經過
OrderBy
向引用屬性的查詢方法附加子句並提供排序方向(Asc
或Desc
)來應用靜態排序。要建立支持動態排序的查詢方法,請參閱「 特殊參數處理 」。
2.4.3。屬性表達式
屬性表達式只能引用被管實體的直接屬性,如上例所示。在查詢建立時,您已經確保解析的屬性是託管域類的屬性。可是,您也能夠經過遍歷嵌套屬性來定義約束。考慮如下方法簽名:
List<Person> findByAddressZipCode(ZipCode zipCode);
假設a Person
有一個Address
with ZipCode
。在這種狀況下,該方法建立屬性遍歷x.address.zipCode
。解析算法首先將整個part(AddressZipCode
)做爲屬性進行解釋,而後檢查該域名是否具備該名稱(未添加大小)。若是算法成功,則使用該屬性。若是不是的話,該算法未來自右側的駱駝案件部分的來源拆分爲頭部和尾部,並嘗試找到相應的屬性 - 在咱們的示例中AddressZip
和Code
。若是算法找到具備該頭部的屬性,它將採用尾部並從該處繼續構建樹,而後按照剛剛描述的方式分割尾部。若是第一次分割不匹配,則算法將分割點移到左側(Address
,ZipCode
)並繼續。
雖然這應該適用於大多數狀況,但算法可能會選擇錯誤的屬性。假設這個Person
類也有一個addressZip
屬性。該算法將在第一輪拆分中匹配,選擇錯誤的屬性,並失敗(由於addressZip
可能沒有code
屬性的類型)。
爲了解決這個歧義,你能夠\_
在你的方法名稱中使用手動定義遍歷點。因此咱們的方法名稱以下所示:
List<Person> findByAddress_ZipCode(ZipCode zipCode);
由於咱們將下劃線字符視爲保留字符,因此咱們強烈建議遵循如下標準Java命名約定(即,不使用屬性名稱中的下劃線,而是使用駝峯大小寫代替)。
2.4.4。特殊參數處理
要處理查詢中的參數,請定義方法參數,如前面的示例中所示。除此以外,基礎設施認可某些特定的類型,如Pageable
和Sort
,動態地應用分頁和排序,以查詢。如下示例演示了這些功能:
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);
The first method lets you pass an org.springframework.data.domain.Pageable
instance to the query method to dynamically add paging to your statically defined query. A Page
knows about the total number of elements and pages available. It does so by the infrastructure triggering a count query to calculate the overall number. As this might be expensive (depending on the store used), you can instead return a Slice
. A Slice
only knows about whether a next Slice
is available, which might be sufficient when walking through a larger result set.
Sorting options are handled through the Pageable
instance, too. If you only need sorting, add an org.springframework.data.domain.Sort
parameter to your method. As you can see, returning a List
is also possible. In this case, the additional metadata required to build the actual Page
instance is not created (which, in turn, means that the additional count query that would have been necessary is not issued). Rather, it restricts the query to look up only the given range of entities.
To find out how many pages you get for an entire query, you have to trigger an additional count query. By default, this query is derived from the query you actually trigger. |
2.4.5. Limiting Query Results
查詢方法的結果能夠經過使用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
關鍵字。此外,對於將結果集限制爲一個實例的查詢,支持將結果封裝到Optional
關鍵字中。
若是將分頁或切片應用於限制查詢分頁(以及計算可用的頁面數量),則將其應用於有限的結果中。
經過使用Sort 參數限制結果與動態排序結合使用,能夠表達「K」最小以及「K」最大元素的查詢方法。 |
2.4.6。流式查詢結果
查詢方法的結果能夠經過使用Java 8 Stream<T>
做爲返回類型來遞增處理。而不是將查詢結果封裝在Stream
數據存儲區中 - 使用特定的方法來執行流式處理,如如下示例所示:
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 潛在包裝底層數據存儲專用資源,所以必須在使用以後被關閉。您能夠Stream 經過使用該close() 方法或使用Java 7 try-with-resources 塊手動關閉,如如下示例所示: |
Stream<T>
try-with-resources塊中的結果
try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) { stream.forEach(…); }
並不是全部的Spring Data模塊當前都支持Stream<T> 做爲返回類型。 |
2.4.7。異步查詢結果
經過使用Spring的異步方法執行功能,能夠異步運行存儲庫查詢。這意味着該方法在調用時當即返回,而實際查詢執行發生在已提交給Spring的任務中TaskExecutor
。異步查詢執行與反應式查詢執行不一樣,不該混用。有關被動支持的更多詳細信息,請參閱商店專用文檔。如下示例顯示了一些異步查詢:
@Async Future<User> findByFirstname(String firstname); @Async CompletableFuture<User> findOneByFirstname(String firstname); @Async ListenableFuture<User> findOneByLastname(String lastname);
使用java.util.concurrent.Future 做爲返回類型。 |
|
使用Java 8 java.util.concurrent.CompletableFuture 做爲返回類型。 |
|
使用a org.springframework.util.concurrent.ListenableFuture 做爲返回類型。 |
2.5。建立存儲庫實例
在本節中,您將爲定義的存儲庫接口建立實例和bean定義。一種方法是使用每一個支持存儲庫機制的Spring Data模塊附帶的Spring命名空間,儘管咱們一般推薦使用Java配置。
2.5.1。XML配置
每一個Spring Data模塊都包含一個repositories
元素,可以讓您定義Spring爲您掃描的基本包,如如下示例所示:
<?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
屬性容許使用通配符,以便您能夠定義掃描軟件包的模式。
使用過濾器
默認狀況下,基礎架構將拾取擴展Repository
位於已配置基礎包下的持久性技術特定子接口的每一個接口,併爲其建立一個bean實例。可是,您可能須要更細緻地控制哪些接口具備爲其建立的bean實例。要作到這一點,使用<include-filter />
和<exclude-filter />
內部元素<repositories />
的元素。語義與Spring的上下文命名空間中的元素徹底等價。有關詳細信息,請參閱這些元素的Spring參考文檔。
例如,要將某些接口從實例化中排除爲存儲庫Bean,可使用如下配置:
<repositories base-package="com.acme.repositories"> <context:exclude-filter type="regex" expression=".*SomeRepository" /> </repositories>
前面的示例排除了全部以SomeRepository
實例化結束的接口。
2.5.2。JavaConfig
存儲庫基礎結構也能夠經過@Enable${store}Repositories
在JavaConfig類上使用特定於商店的註釋來觸發。有關Spring容器的基於Java的配置的介紹,請參閱Spring參考文檔中的JavaConfig。
啓用Spring Data存儲庫的示例配置相似於如下內容:
@Configuration @EnableJpaRepositories("com.acme.repositories") class ApplicationConfiguration { @Bean EntityManagerFactory entityManagerFactory() { // … } }
前面的示例使用JPA特定的註釋,它將根據您實際使用的商店模塊進行更改。這一樣適用於EntityManagerFactory bean的定義。請參閱包含特定於商店的配置的章節。 |
2.5.3。獨立使用
您還能夠在Spring容器以外使用存儲庫基礎結構 - 例如,在CDI環境中。你的類路徑中仍然須要一些Spring庫,可是,一般你也能夠經過編程來設置庫。提供存儲庫支持的Spring Data模塊提供了一個特定RepositoryFactory
於您可使用的持久性技術,以下所示:
RepositoryFactorySupport factory = … // Instantiate factory here UserRepository repository = factory.getRepository(UserRepository.class);
2.6。Spring數據倉庫的自定義實現
本節涵蓋存儲庫自定義以及片斷如何造成組合存儲庫。
當查詢方法須要不一樣的行爲或沒法經過查詢派生實現時,則須要提供自定義實現。Spring Data存儲庫容許您提供自定義存儲庫代碼並將其與通用CRUD抽象和查詢方法功能集成。
2.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(例如a JdbcTemplate
)注入引用,參與方面等等。
您可讓存儲庫接口擴展片斷接口,如如下示例所示:
interface UserRepository extends CrudRepository<User, Long>, CustomizedUserRepository { // Declare query methods here }
使用存儲庫接口擴展分段接口將CRUD和自定義功能結合起來,並將其提供給客戶端。
Spring Data repositories are implemented by using fragments that form a repository composition. Fragments are the base repository, functional aspects (such as QueryDsl), and custom interfaces along with their implementation. Each time you add an interface to your repository interface, you enhance the composition by adding a fragment. The base repository and repository aspect implementations are provided by each Spring Data module.
The following example shows custom interfaces and their implementations:
interface HumanRepository { void someHumanMethod(User user); } class HumanRepositoryImpl implements HumanRepository { public void someHumanMethod(User user) { // Your custom implementation } } interface ContactRepository { void someContactMethod(User user); User anotherContactMethod(User user); } class ContactRepositoryImpl implements ContactRepository { public void someContactMethod(User user) { // Your custom implementation } public User anotherContactMethod(User user) { // Your custom implementation } }
The following example shows the interface for a custom repository that extends CrudRepository
:
interface UserRepository extends CrudRepository<User, Long>, HumanRepository, ContactRepository { // Declare query methods here }
Repositories may be composed of multiple custom implementations that are imported in the order of their declaration. Custom implementations have a higher priority than the base implementation and repository aspects. This ordering lets you override base repository and aspect methods and resolves ambiguity if two fragments contribute the same method signature. Repository fragments are not limited to use in a single repository interface. Multiple repositories may use a fragment interface, letting you reuse customizations across different repositories.
The following example shows a repository fragment and its implementation:
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 } }
The following example shows a repository that uses the preceding repository fragment:
interface UserRepository extends CrudRepository<User, Long>, CustomizedSave<User> { } interface PersonRepository extends CrudRepository<Person, Long>, CustomizedSave<Person> { }
Configuration
If you use namespace configuration, the repository infrastructure tries to autodetect custom implementation fragments by scanning for classes below the package in which it found a repository. These classes need to follow the naming convention of appending the namespace element’s repository-impl-postfix
attribute to the fragment interface name. This postfix defaults to Impl
. The following example shows a repository that uses the default postfix and a repository that sets a custom value for the postfix:
<repositories base-package="com.acme.repository" /> <repositories base-package="com.acme.repository" repository-impl-postfix="MyPostfix" />
The first configuration in the preceding example tries to look up a class called com.acme.repository.CustomizedUserRepositoryImpl
to act as a custom repository implementation. The second example tries to lookup com.acme.repository.CustomizedUserRepositoryMyPostfix
.
Resolution of Ambiguity
If multiple implementations with matching class names are found in different packages, Spring Data uses the bean names to identify which one to use.
Given the following two custom implementations for the CustomizedUserRepository
shown earlier, the first implementation is used. Its bean name is customizedUserRepositoryImpl
, which matches that of the fragment interface (CustomizedUserRepository
) plus the postfix 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 }
If you annotate the UserRepository
interface with @Component("specialCustom")
, the bean name plus Impl
then matches the one defined for the repository implementation in com.acme.impl.two
, and it is used instead of the first one.
Manual Wiring
If your custom implementation uses annotation-based configuration and autowiring only, the preceding approach shown works well, because it is treated as any other Spring bean. If your implementation fragment bean needs special wiring, you can declare the bean and name it according to the conventions described in the preceding section. The infrastructure then refers to the manually defined bean definition by name instead of creating one itself. The following example shows how to manually wire a custom implementation:
<repositories base-package="com.acme.repository" /> <beans:bean id="userRepositoryImpl" class="…"> <!-- further configuration --> </beans:bean>
2.6.2. Customize the Base Repository
上一節中描述的方法須要在定製基本存儲庫行爲時定製每一個存儲庫接口,以便全部存儲庫都受到影響。爲了改變全部存儲庫的行爲,能夠建立一個擴展持久性技術特定存儲庫基類的實現。而後,該類將做爲存儲庫代理的自定義基類,如如下示例所示:
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 } }
該類須要具備特定於存儲庫的工廠實現使用的超類的構造函數。若是存儲庫基類具備多個構造函數,請覆蓋採用EntityInformation 加特定於存儲庫的基礎結構對象(如一個EntityManager 或模板類)的構造函數。 |
最後一步是讓Spring Data基礎設施知道定製的存儲庫基類。在Java配置中,您可使用註釋的repositoryBaseClass
屬性來完成此操做@Enable${store}Repositories
,如如下示例所示:
@Configuration @EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class) class ApplicationConfiguration { … }
XML名稱空間中提供了相應的屬性,如如下示例所示:
<repositories base-package="com.acme.repository" base-class="….MyRepositoryImpl" />
2.7。從聚合根發佈事件
由存儲庫管理的實體是聚合根。在Domain-Driven Design應用程序中,這些聚合根一般會發布域事件。Spring Data提供了一個註釋,稱爲@DomainEvents
可用於聚合根方法的註釋,以使該出版物儘量地簡單,如如下示例所示:
class AnAggregateRoot { @DomainEvents Collection<Object> domainEvents() { // … return events you want to get published here } @AfterDomainEventPublication void callbackMethod() { // … potentially clean up domain events list } }
使用該方法@DomainEvents 能夠返回單個事件實例或事件集合。它不能有任何爭論。 |
|
全部事件發佈後,咱們都有註解的方法@AfterDomainEventPublication 。它可用於潛在地清理要發佈的事件列表(以及其餘用途)。 |
每次調用Spring Data存儲庫的save(…)
方法時,都會調用這些方法。
2.8。Spring數據擴展
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 that enables the construction of statically typed SQL-like queries through its fluent API.
Several Spring Data modules offer integration with Querydsl through QuerydslPredicateExecutor
, as shown in the following example:
public interface QuerydslPredicateExecutor<T> { Optional<T> findById(Predicate predicate); Iterable<T> findAll(Predicate predicate); long count(Predicate predicate); boolean exists(Predicate predicate); // … more functionality omitted. }
Finds and returns a single entity matching the Predicate . |
|
Finds and returns all entities matching the Predicate . |
|
Returns the number of entities matching the Predicate . |
|
Returns whether an entity that matches the Predicate exists. |
To make use of Querydsl support, extend QuerydslPredicateExecutor
on your repository interface, as shown in the following example
interface UserRepository extends CrudRepository<User, Long>, QuerydslPredicateExecutor<User> { }
The preceding example lets you write typesafe queries using Querydsl Predicate
instances, as shown in the following example:
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 in the current (and later) versions of Spring Data Commons. As the newly introduced support changes many things, we kept the documentation of the former behavior in [web.legacy]. |
支持存儲庫編程模型的Spring Data模塊提供各類Web支持。Web相關組件須要將Spring MVC JAR放在類路徑中。其中一些甚至提供與Spring HATEOAS的集成。一般,經過@EnableSpringDataWebSupport
在JavaConfig配置類中使用註釋來啓用集成支持,如如下示例所示:
@Configuration @EnableWebMvc @EnableSpringDataWebSupport class WebConfiguration {}
該@EnableSpringDataWebSupport
批註註冊幾個組件,咱們將在一個位討論。它也會在類路徑中檢測到Spring HATEOAS,併爲其註冊集成組件。
另外,若是你使用XML配置,註冊一個SpringDataWebConfiguration
或者HateoasAwareSpringDataWebConfiguration
做爲Spring bean,以下例(for SpringDataWebConfiguration
)所示:
<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" /> <!-- If you use Spring HATEOAS, register this one *instead* of the former --> <bean class="org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration" />
基本的Web支持
上一節中顯示的配置註冊了一些基本組件:
-
A
DomainClassConverter
讓Spring MVC從請求參數或路徑變量中解析由存儲庫管理的域類的實例。 -
HandlerMethodArgumentResolver
實現讓Spring MVC的決心Pageable
和Sort
實例從請求參數。
DomainClassConverter
將DomainClassConverter
讓你在Spring MVC中的控制器方法簽名使用域類型直接,讓你不用經過儲存手動查找的狀況下,如在下面的例子:
@Controller @RequestMapping("/users") class UserController { @RequestMapping("/{id}") String showUserForm(@PathVariable("id") User user, Model model) { model.addAttribute("user", user); return "userForm"; } }
如您所見,該方法User
直接接收實例,不須要進一步查找。經過讓Spring MVC首先將路徑變量轉換爲id
域類的類型,並最終經過調用findById(…)
爲域類型註冊的存儲庫實例來訪問實例,能夠解決該實例。
目前,存儲庫必須實施CrudRepository 纔有資格被發現以進行轉換。 |
用於Pageable和Sort的HandlerMethodArgumentResolvers
上一節中顯示的配置代碼片斷也會註冊一個PageableHandlerMethodArgumentResolver
以及一個實例SortHandlerMethodArgumentResolver
。註冊啓用Pageable
並Sort
做爲有效的控制器方法參數,如如下示例所示:
@Controller @RequestMapping("/users") class UserController { private final UserRepository repository; UserController(UserRepository repository) { this.repository = repository; } @RequestMapping String showUsers(Model model, Pageable pageable) { model.addAttribute("users", repository.findAll(pageable)); return "users"; } }
前面的方法簽名會致使Spring MVC嘗試Pageable
使用如下默認配置從請求參數派生實例:
|
您想要檢索的頁面。0索引,默認爲0。 |
|
您要檢索的頁面大小。默認爲20。 |
|
應該按格式排序的屬性 |
要定製這種行爲,分別註冊實現PageableHandlerMethodArgumentResolverCustomizer
接口或SortHandlerMethodArgumentResolverCustomizer
接口的bean 。它的customize()
方法被調用,讓你改變設置,以下例所示:
@Bean SortHandlerMethodArgumentResolverCustomizer sortCustomizer() { return s -> s.setPropertyDelimiter("<-->"); }
若是設置現有屬性MethodArgumentResolver
不足以達到您的目的,請擴展其中一個SpringDataWebConfiguration
或啓用HATEOAS的等效項,覆蓋pageableResolver()
或sortResolver()
方法,而後導入您的自定義配置文件,而不是使用@Enable
註釋。
若是您須要從請求中解析多個Pageable
或Sort
實例(例如,針對多個表),則可使用Spring的@Qualifier
註釋區分一個。而後請求參數必須之前綴${qualifier}_
。如下示例顯示了生成的方法簽名:
String showUsers(Model model, @Qualifier("thing1") Pageable first, @Qualifier("thing2") Pageable second) { … }
你有填充thing1_page
和thing2_page
等。
Pageable
傳遞給方法的默認值等於a,new PageRequest(0, 20)
但可使用參數@PageableDefault
上的註釋進行自定義Pageable
。
超媒體支持Pageables
Spring HATEOAS附帶一個表示模型class(PagedResources
),它容許Page
使用必要的Page
元數據豐富實例的內容以及讓客戶端輕鬆導航頁面的連接。將頁面轉換爲a PagedResources
由Spring HATEOAS ResourceAssembler
接口的實現完成,該接口稱爲PagedResourcesAssembler
。如下示例顯示如何使用a PagedResourcesAssembler
做爲控制器方法參數:
@Controller class PersonController { @Autowired PersonRepository repository; @RequestMapping(value = "/persons", method = RequestMethod.GET) HttpEntity<PagedResources<Person>> persons(Pageable pageable, PagedResourcesAssembler assembler) { Page<Person> persons = repository.findAll(pageable); return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK); } }
如上例所示啓用配置,能夠將PagedResourcesAssembler
其用做控制器方法參數。調用toResources(…)
它有如下效果:
-
該內容
Page
成爲該PagedResources
實例的內容。 -
該
PagedResources
對象得到一個PageMetadata
附加的實例,並使用來自Page
和底層的信息填充該對象PageRequest
。 -
將
PagedResources
可能會prev
和next
鏈接鏈路,根據頁面的狀態。連接指向方法映射到的URI。添加到該方法中的分頁參數與該設置相匹配,PageableHandlerMethodArgumentResolver
以確保之後能夠解析連接。
假設咱們在數據庫中有30個Person實例。您如今能夠觸發請求()並查看與如下內容相似的輸出:GEThttp://localhost:8080/persons
{ "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 } }
您會看到彙編程序生成了正確的URI,而且還選取了缺省配置將參數解析Pageable
爲一個即將到來的請求。這意味着,若是您更改該配置,連接將自動遵照更改。默認狀況下,彙編程序指向它所調用的控制器方法,但能夠經過交付自定義Link
來做爲基礎來構建分頁連接,從而重載該PagedResourcesAssembler.toResource(…)
方法。
Web數據綁定支持
經過使用JSONPath表達式(須要Jayway JsonPath或XPath表達式(須要XmlBeam)),Spring數據投影(在Projections中進行了介紹)可用於綁定傳入的請求負載,如如下示例所示:
@ProjectedPayload public interface UserPayload { @XBRead("//firstname") @JsonPath("$..firstname") String getFirstname(); @XBRead("/lastname") @JsonPath({ "$.lastname", "$.user.lastname" }) String getLastname(); }
上例中顯示的類型能夠用做Spring MVC處理方法參數,或者經過使用ParameterizedTypeReference
其中一個RestTemplate
方法。前面的方法聲明將嘗試firstname
在給定文檔中的任何位置查找。該lastname
XML查詢是對輸入文檔的頂層進行。它的JSON變體lastname
首先嘗試頂層,但若是前一層沒有返回值,也會嘗試lastname
嵌套在user
子文檔中。這樣,源文檔結構的變化能夠輕鬆地被緩解,而無需客戶端調用公開的方法(一般是基於類的有效載荷綁定的缺點)。
Nested projections are supported as described in Projections. If the method returns a complex, non-interface type, a Jackson ObjectMapper
is used to map the final value.
For Spring MVC, the necessary converters are registered automatically as soon as @EnableSpringDataWebSupport
is active and the required dependencies are available on the classpath. For usage with RestTemplate
, register a ProjectingJackson2HttpMessageConverter
(JSON) or XmlBeamHttpMessageConverter
manually.
For more information, see the web projection example in the canonical Spring Data Examples repository.
Querydsl Web Support
For those stores having QueryDSL integration, it is possible to derive queries from the attributes contained in a Request
query string.
Consider the following query string:
?firstname=Dave&lastname=Matthews
給定User
前面例子中的對象,查詢字符串能夠經過使用QuerydslPredicateArgumentResolver
。解析爲下列值。
QUser.user.firstname.eq("Dave").and(QUser.user.lastname.eq("Matthews"))
該功能會自動啓用,以及@EnableSpringDataWebSupport 在類路徑中找到Querydsl時。 |
向@QuerydslPredicate
方法簽名中添加a 提供了一個Predicate
可當即使用的方法,能夠使用該方法運行QuerydslPredicateExecutor
。
類型信息一般從方法的返回類型中解析出來。因爲該信息不必定與域類型匹配,所以使用該root 屬性多是一個好主意QuerydslPredicate 。 |
如下示例顯示瞭如何@QuerydslPredicate
在方法簽名中使用:
@Controller class UserController { @Autowired UserRepository repository; @RequestMapping(value = "/", method = RequestMethod.GET) String index(Model model, @QuerydslPredicate(root = User.class) Predicate predicate, Pageable pageable, @RequestParam MultiValueMap<String, String> parameters) { model.addAttribute("users", repository.findAll(predicate, pageable)); return "index"; } }
解析查詢字符串參數匹配Predicate 的User 。 |
默認綁定以下:
-
Object
在簡單的屬性上eq
。 -
Object
關於集合像屬性同樣contains
。 -
Collection
在簡單的屬性上in
。
這些綁定能夠經過Java 8 的bindings
屬性@QuerydslPredicate
或經過使用Java 8 default methods
並將該QuerydslBinderCustomizer
方法添加到存儲庫接口來定製。
interface UserRepository extends CrudRepository<User, String>, QuerydslPredicateExecutor<User>, QuerydslBinderCustomizer<QUser> { @Override default void customize(QuerydslBindings bindings, QUser user) { bindings.bind(user.username).first((path, value) -> path.contains(value)) bindings.bind(String.class) .first((StringPath path, String value) -> path.containsIgnoreCase(value)); bindings.excluding(user.password); } }
QuerydslPredicateExecutor 提供對特定查找方法的訪問Predicate 。 |
|
QuerydslBinderCustomizer 在倉庫界面上定義的是自動拾取和快捷方式@QuerydslPredicate(bindings=…) 。 |
|
將該username 屬性的綁定定義爲簡單contains 綁定。 |
|
將String 屬性的默認綁定定義爲不區分大小寫的contains 匹配。 |
|
password 從Predicate 解決方案中排除財產。 |
2.8.3。存儲庫Poppop
若是您使用Spring JDBC模塊,那麼您可能很熟悉DataSource
使用SQL腳本填充的支持。儘管存儲庫級別沒有使用SQL做爲數據定義語言,但相似的抽象是可用的,由於它必須是獨立於存儲的。所以,populators支持XML(經過Spring的OXM抽象)和JSON(經過Jackson)來定義數據來填充存儲庫。
假設您擁有data.json
包含如下內容的文件:
[ { "_class" : "com.acme.Person", "firstname" : "Dave", "lastname" : "Matthews" }, { "_class" : "com.acme.Person", "firstname" : "Carter", "lastname" : "Beauford" } ]
您可使用Spring Data Commons中提供的存儲庫名稱空間的populator元素來填充存儲庫。要將前面的數據填充到PersonRepository中,請聲明相似於如下內容的populator:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="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-populator locations="classpath:data.json" /> </beans>
前面的聲明致使data.json
文件被Jackson讀取和反序列化ObjectMapper
。
經過檢查\_class
JSON文檔的屬性來肯定JSON對象被解組的類型。基礎結構最終選擇適當的存儲庫來處理反序列化的對象。
要改成使用XML定義存儲庫應該填充的數據,可使用該unmarshaller-populator
元素。您將其配置爲使用Spring OXM中提供的XML編組器之一。有關詳細信息,請參閱Spring參考文檔。如下示例顯示如何使用JAXB解組存儲器加載器:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="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-populator locations="classpath:data.json" unmarshaller-ref="unmarshaller" /> <oxm:jaxb2-marshaller contextPath="com.acme" /> </beans>
參考文檔
3. JPA存儲庫
本章指出了JPA存儲庫支持的特色。這基於「 使用Spring數據存儲庫 」中介紹的核心存儲庫支持。確保你對這裏解釋的基本概念有充分的理解。
3.1。介紹
本節經過如下任一介紹了配置Spring Data JPA的基礎知識:
-
「 Spring命名空間 」(XML配置)
-
「 基於註釋的配置 」(Java配置)
3.1.1。Spring命名空間
Spring Data的JPA模塊包含一個容許定義存儲庫bean的自定義名稱空間。它還包含特定於JPA的某些功能和元素屬性。一般,可使用該repositories
元素來設置JPA存儲庫,如如下示例所示:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="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:repositories base-package="com.acme.repositories" /> </beans>
使用該repositories
元素查找Spring Data存儲庫,如「 建立存儲庫實例 」中所述。除此以外,它還爲全部註釋的bean激活持久性異常轉換@Repository
,讓JPA持久性提供者拋出的異常轉換爲Spring的DataAccessException
層次結構。
自定義命名空間屬性
Beyond the default attributes of the repositories
element, the JPA namespace offers additional attributes to let you gain more detailed control over the setup of the repositories:
|
Explicitly wire the |
|
Explicitly wire the |
Spring Data JPA requires a PlatformTransactionManager bean named transactionManager to be present if no explicit transaction-manager-ref is defined. |
3.1.2. Annotation-based Configuration
The Spring Data JPA repositories support can be activated not only through an XML namespace but also by using an annotation through JavaConfig, as shown in the following example:
@Configuration @EnableJpaRepositories @EnableTransactionManagement class ApplicationConfig { @Bean public DataSource dataSource() { EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); return builder.setType(EmbeddedDatabaseType.HSQL).build(); } @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); vendorAdapter.setGenerateDdl(true); LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); factory.setJpaVendorAdapter(vendorAdapter); factory.setPackagesToScan("com.acme.domain"); factory.setDataSource(dataSource()); return factory; } @Bean public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { JpaTransactionManager txManager = new JpaTransactionManager(); txManager.setEntityManagerFactory(entityManagerFactory); return txManager; } }
You must create LocalContainerEntityManagerFactoryBean and not EntityManagerFactory directly, since the former also participates in exception translation mechanisms in addition to creating EntityManagerFactory . |
The preceding configuration class sets up an embedded HSQL database by using the EmbeddedDatabaseBuilder
API of spring-jdbc
. Spring Data then sets up an EntityManagerFactory
and uses Hibernate as the sample persistence provider. The last infrastructure component declared here is the JpaTransactionManager
. Finally, the example activates Spring Data JPA repositories by using the @EnableJpaRepositories
annotation, which essentially carries the same attributes as the XML namespace. If no base package is configured, it uses the one in which the configuration class resides.
3.2. Persisting Entities
This section describes how to persist (save) entities with Spring Data JPA.
3.2.1. Saving Entities
Saving an entity can be performed with the CrudRepository.save(…)
method. It persists or merges the given entity by using the underlying JPA EntityManager
. If the entity has not yet been persisted, Spring Data JPA saves the entity with a call to the entityManager.persist(…)
method. Otherwise, it calls the entityManager.merge(…)
method.
Entity State-detection Strategies
Spring Data JPA offers the following strategies to detect whether an entity is new or not:
-
Id-Property inspection (default): By default Spring Data JPA inspects the identifier property of the given entity. If the identifier property is
null
, then the entity is assumed to be new. Otherwise, it is assumed to be not new. -
Implementing
Persistable
: If an entity implementsPersistable
, Spring Data JPA delegates the new detection to theisNew(…)
method of the entity. See the JavaDoc for details. -
Implementing
EntityInformation
: You can customize theEntityInformation
abstraction used in theSimpleJpaRepository
implementation by creating a subclass ofJpaRepositoryFactory
and overriding thegetEntityInformation(…)
method accordingly. You then have to register the custom implementation ofJpaRepositoryFactory
as a Spring bean. Note that this should be rarely necessary. See the JavaDoc for details.
3.3. Query Methods
This section describes the various ways to create a query with Spring Data JPA.
3.3.1. Query Lookup Strategies
The JPA module supports defining a query manually as a String or having it being derived from the method name.
3.3.2。查詢建立
一般,JPA的查詢建立機制按「 查詢方法 」中的描述工做。如下示例顯示了JPA查詢方法轉換爲的內容:
公共接口UserRepository擴展了Repository <User,Long> { List <User> findByEmailAddressAndLastname(String emailAddress,String lastname); }
咱們使用JPA標準API從這個建立了一個查詢,但實際上,這轉換成如下查詢:select u from User u where u.emailAddress = ?1 and u.lastname = ?2
。Spring Data JPA執行屬性檢查並遍歷嵌套屬性,如「 屬性表達式 」中所述。
下表描述了JPA支持的關鍵字以及包含該關鍵字的方法轉換爲:
關鍵詞 | 樣品 | JPQL片斷 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In and NotIn also take any subclass of Collection as aparameter as well as arrays or varargs. For other syntactical versions of the same logical operator, check 「Repository query keywords」. |
3.3.3. Using JPA Named Queries
The examples use the <named-query /> element and @NamedQuery annotation. The queries for these configuration elements have to be defined in the JPA query language. Of course, you can use <named-native-query /> or @NamedNativeQuery too. These elements let you define the query in native SQL by losing the database platform independence. |
XML Named Query Definition
To use XML configuration, add the necessary <named-query />
element to the orm.xml
JPA configuration file located in the META-INF
folder of your classpath. Automatic invocation of named queries is enabled by using some defined naming convention. For more details, see below.
<named-query name="User.findByLastname"> <query>select u from User u where u.lastname = ?1</query> </named-query>
The query has a special name that is used to resolve it at runtime.
Annotation-based Configuration
Annotation-based configuration has the advantage of not needing another configuration file to be edited, lowering maintenance effort. You pay for that benefit by the need to recompile your domain class for every new query declaration.
@Entity @NamedQuery(name = "User.findByEmailAddress", query = "select u from User u where u.emailAddress = ?1") public class User { }
Declaring Interfaces
To allow execution of these named queries, specify the UserRepository
as follows:
public interface UserRepository extends JpaRepository<User, Long> { List<User> findByLastname(String lastname); User findByEmailAddress(String emailAddress); }
Spring Data嘗試將對這些方法的調用解析爲命名查詢,從配置的域類的簡單名稱開始,接着用點分隔方法名稱。所以,前面的示例將使用examlpe中定義的命名查詢,而不是嘗試從方法名稱建立查詢。
3.3.4。運用@Query
使用命名查詢來聲明實體查詢是一種有效的方法,對於少許查詢來講工做正常。因爲查詢自己與執行它們的Java方法綁定,所以能夠經過使用Spring Data JPA @Query
註釋直接綁定它們,而不是將它們註釋到域類。這將持久性特定信息從域類中釋放出來,並將查詢共同定位到存儲庫接口。
查詢註釋到查詢方法的查詢優先於使用@NamedQuery
或聲明的查詢定義的查詢orm.xml
。
如下示例顯示了使用@Query
註釋建立的查詢:
@Query
public interface UserRepository extends JpaRepository<User, Long> { @Query("select u from User u where u.emailAddress = ?1") User findByEmailAddress(String emailAddress); }
使用高級LIKE
表達式
建立的手動定義查詢的查詢執行機制@Query
容許LIKE
在查詢定義內定義高級表達式,如如下示例所示:
like
@Query中的高級表達式
public interface UserRepository extends JpaRepository<User, Long> { @Query("select u from User u where u.firstname like %?1") List<User> findByFirstnameEndsWith(String firstname); }
在前面的示例中,能夠識別LIKE
分隔符(%
),並將查詢轉換爲有效的JPQL查詢(刪除%
)。在查詢執行後,傳遞給方法調用的參數將得到先前識別的LIKE
模式的加強。
原生查詢
該@Query
註釋容許經過設定運行的原生查詢nativeQuery
標誌設置爲true,如圖如下示例:
public interface UserRepository extends JpaRepository<User, Long> { @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true) User findByEmailAddress(String emailAddress); }
Spring Data JPA當前不支持對本機查詢進行動態排序,由於它必須處理已聲明的實際查詢,對於本機SQL而言,它沒法可靠地進行操做。可是,您能夠經過本身指定計數查詢來使用本機查詢進行分頁,如如下示例所示: |
@Query
public interface UserRepository extends JpaRepository<User, Long> { @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1", countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1", nativeQuery = true) Page<User> findByLastname(String lastname, Pageable pageable); }
經過將.count
後綴添加到查詢副本中,相似的方法也適用於命名的本機查詢。不過,您可能須要爲計數查詢註冊結果集映射。
3.3.5。使用排序
排序能夠經過提供PageRequest
或Sort
直接使用來完成。Order
實例中實際使用的屬性Sort
須要與您的域模型匹配,這意味着它們須要解析爲查詢中使用的屬性或別名。JPQL將其定義爲狀態字段路徑表達式。
使用任何不可引用的路徑表達式會致使一個Exception 。 |
可是,Sort
一塊兒使用@Query
可讓您潛入Order
包含ORDER BY
子句中的函數的未經路徑檢查的實例中。這是可能的,由於Order
它附加到給定的查詢字符串。默認狀況下,Spring Data JPA拒絕Order
包含函數調用的任何實例,但能夠JpaSort.unsafe
用來添加潛在的不安全排序。
如下示例使用Sort
和JpaSort
,包括一個不安全的選項JpaSort
:
Sort
和
JpaSort
public interface UserRepository extends JpaRepository<User, Long> { @Query("select u from User u where u.lastname like ?1%") List<User> findByAndSort(String lastname, Sort sort); @Query("select u.id, LENGTH(u.firstname) as fn_len from User u where u.lastname like ?1%") List<Object[]> findByAsArrayAndSort(String lastname, Sort sort); } repo.findByAndSort("lannister", new Sort("firstname")); repo.findByAndSort("stark", new Sort("LENGTH(firstname)")); repo.findByAndSort("targaryen", JpaSort.unsafe("LENGTH(firstname)")); repo.findByAsArrayAndSort("bolton", new Sort("fn_len"));
有效Sort 表達式指向域模型中的屬性。 |
|
無效的Sort 包含函數調用。例外。 |
|
Sort 包含明確不安全的 有效Order 。 |
|
Sort 指向別名函數的有效表達式。 |
3.3.6。使用命名參數
默認狀況下,Spring Data JPA使用基於位置的參數綁定,如前面全部示例中所述。當對參數位置進行重構時,這使得查詢方法有點容易出錯。要解決此問題,可使用@Param
註釋爲方法參數提供具體名稱,並在查詢中綁定名稱,如如下示例中所示:
public interface UserRepository extends JpaRepository<User, Long> { @Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname") User findByLastnameOrFirstname(@Param("lastname") String lastname, @Param("firstname") String firstname); }
方法參數根據在定義的查詢中的順序進行切換。 |
從版本4開始,Spring徹底支持基於-parameters 編譯器標誌的Java 8參數名稱發現。經過在構建中使用此標誌做爲調試信息的替代方法,能夠省略@Param 命名參數的註釋。 |
3.3.7。使用SpEL表達式
As of Spring Data JPA release 1.4, we support the usage of restricted SpEL template expressions in manually defined queries that are defined with @Query
. Upon query execution, these expressions are evaluated against a predefined set of variables. Spring Data JPA supports a variable called entityName
. Its usage is select x from #{#entityName} x
. It inserts the entityName
of the domain type associated with the given repository. The entityName
is resolved as follows: If the domain type has set the name property on the @Entity
annotation, it is used. Otherwise, the simple class-name of the domain type is used.
The following example demonstrates one use case for the #{#entityName}
expression in a query string where you want to define a repository interface with a query method and a manually defined query:
@Entity public class User { @Id @GeneratedValue Long id; String lastname; } public interface UserRepository extends JpaRepository<User,Long> { @Query("select u from #{#entityName} u where u.lastname = ?1") List<User> findByLastname(String lastname); }
爲避免在@Query
註釋的查詢字符串中陳述實際的實體名稱,可使用該#{#entityName}
變量。
該entityName 能夠經過使用定製@Entity 的註釋。orm.xml SpEL表達式不支持自定義。 |
固然,您能夠User
直接在查詢聲明中使用,但這也須要您更改查詢。該類引用#entityName
將User
類的潛在將來從新映射轉換爲不一樣的實體名稱(例如,經過使用@Entity(name = "MyUser")
。
#{#entityName}
查詢字符串中表達式的另外一個用例是,若是要爲具體的域類型定義具備專用存儲庫接口的通用存儲庫接口。爲了避免重複定義具體接口上的自定義查詢方法,能夠@Query
在通用資源庫接口中的註釋的查詢字符串中使用實體名稱表達式,如如下示例所示:
@MappedSuperclass public abstract class AbstractMappedType { … String attribute } @Entity public class ConcreteType extends AbstractMappedType { … } @NoRepositoryBean public interface MappedTypeRepository<T extends AbstractMappedType> extends Repository<T, Long> { @Query("select t from #{#entityName} t where t.attribute = ?1") List<T> findAllByAttribute(String attribute); } public interface ConcreteRepository extends MappedTypeRepository<ConcreteType> { … }
在前面的例子中,MappedTypeRepository
接口是擴展了幾個域類型的公共父接口AbstractMappedType
。它還定義了findAllByAttribute(…)
可用於專用存儲庫接口實例的通用方法。若是您如今調用findByAllAttribute(…)
上ConcreteRepository
,查詢變得select t from ConcreteType t where t.attribute = ?1
。
3.3.8。修改查詢
全部前面的部分都描述瞭如何聲明查詢來訪問給定實體或實體集合。您可使用「 自定義Spring數據存儲庫實現 」中描述的工具來添加自定義修改行爲。因爲此方法對於全面的自定義功能是可行的,所以您能夠經過註釋查詢方法來修改僅須要參數綁定的查詢@Modifying
,如如下示例所示:
@Modifying @Query("update User u set u.firstname = ?1 where u.lastname = ?2") int setFixedFirstnameFor(String firstname, String lastname);
這樣作會觸發將註釋到方法的查詢做爲更新查詢,而不是選擇查詢。因爲在EntityManager
修改查詢執行後可能包含過期的實體,所以咱們不會自動將其清除(請參閱JavaDoc of EntityManager.clear()
的詳細信息),由於這會有效地刪除全部未更新的更改,這些更改仍在掛起EntityManager
。若是您但願EntityManager
自動清除,能夠將@Modifying
註釋的clearAutomatically
屬性設置爲true
。
派生刪除查詢
Spring Data JPA還支持導出的刪除查詢,能夠避免顯式聲明JPQL查詢,如如下示例所示:
interface UserRepository extends Repository<User, Long> { void deleteByRoleId(long roleId); @Modifying @Query("delete from User u where user.role.id = ?1") void deleteInBulkByRoleId(long roleId); }
雖然這個deleteByRoleId(…)
方法看起來基本上和它產生了相同的結果deleteInBulkByRoleId(…)
,可是這兩個方法聲明在執行方式上存在着重要的區別。顧名思義,後一種方法針對數據庫發出單個JPQL查詢(在註釋中定義的查詢)。這意味着即便是當前加載的實例User
也沒有看到調用的生命週期回調。
爲了確保實際調用生命週期查詢,調用deleteByRoleId(…)
執行查詢,而後逐個刪除返回的實例,以便持久性提供者能夠實際調用@PreRemove
這些實體的回調。
實際上,派生刪除查詢是執行查詢而後調用CrudRepository.delete(Iterable<User> users)
結果並保持行爲與其餘delete(…)
方法的實現同步的快捷方式CrudRepository
。
3.3.9。應用查詢提示
要將JPA查詢提示應用於存儲庫接口中聲明的查詢,可使用@QueryHints
註釋。它須要一個JPA @QueryHint
批註數組和一個布爾標誌來禁用應用於應用分頁時觸發的附加計數查詢的提示,如如下示例所示:
public interface UserRepository extends Repository<User, Long> { @QueryHints(value = { @QueryHint(name = "name", value = "value")}, forCounting = false) Page<User> findByLastname(String lastname, Pageable pageable); }
上述聲明將應用@QueryHint
爲實際查詢配置的值,但省略將其應用於觸發的計數查詢以計算總頁數。
3.3.10。配置提取和LoadGraphs
JPA 2.1規範引入了對指定Fetch和LoadGraphs的支持,咱們也經過@EntityGraph
註釋支持它,它容許您引用@NamedEntityGraph
定義。您能夠在實體上使用該註釋來配置生成的查詢的提取計劃。獲取的類型(Fetch
或Load
)可使用註釋中的type
屬性進行配置@EntityGraph
。請參閱JPA 2.1規範3.7.4以供進一步參考。
如下示例顯示如何在實體上定義命名實體圖:
@Entity @NamedEntityGraph(name = "GroupInfo.detail", attributeNodes = @NamedAttributeNode("members")) public class GroupInfo { // default fetch mode is lazy. @ManyToMany List<GroupMember> members = new ArrayList<GroupMember>(); … }
如下示例顯示如何引用存儲庫查詢方法上的命名實體圖:
@Repository public interface GroupRepository extends CrudRepository<GroupInfo, String> { @EntityGraph(value = "GroupInfo.detail", type = EntityGraphType.LOAD) GroupInfo getByGroupName(String name); }
也能夠經過使用來定義臨時實體圖@EntityGraph
。提供attributePaths
的內容EntityGraph
不須要顯式添加@NamedEntityGraph
到您的域類型就能夠翻譯成相應的內容,如如下示例所示:
@Repository public interface GroupRepository extends CrudRepository<GroupInfo, String> { @EntityGraph(attributePaths = { "members" }) GroupInfo getByGroupName(String name); }
3.3.11。預測
Spring Data查詢方法一般會返回存儲庫管理的聚合根的一個或多個實例。可是,有時可能須要根據這些類型的某些屬性建立投影。Spring Data容許對專用返回類型進行建模,以更有選擇地檢索托管集合的部分視圖。
設想一個存儲庫和聚合根類型,例如如下示例:
class Person { @Id UUID id; String firstname, lastname; Address address; static class Address { String zipCode, city, street; } } interface PersonRepository extends Repository<Person, UUID> { Collection<Person> findByLastname(String lastname); }
如今想象一下,咱們只想檢索人的姓名屬性。Spring Data提供了什麼手段來實現這一目標?本章的其他部分回答了這個問題。
基於接口的投影
將查詢結果僅限於名稱屬性的最簡單方法是聲明一個接口,該接口公開要讀取的屬性的訪問器方法,如如下示例所示:
interface NamesOnly { String getFirstname(); String getLastname(); }
這裏重要的一點是,這裏定義的屬性徹底匹配聚合根中的屬性。這樣作能夠添加查詢方法,以下所示:
interface PersonRepository extends Repository<Person, UUID> { Collection<NamesOnly> findByLastname(String lastname); }
查詢執行引擎在運行時爲每一個返回的元素建立該接口的代理實例,並將調用轉發給目標對象的公開方法。
能夠遞歸地使用投影。若是您還想包含一些Address
信息,請爲其建立一個投影界面,並從聲明中返回該界面getAddress()
,如如下示例所示:
interface PersonSummary { String getFirstname(); String getLastname(); AddressSummary getAddress(); interface AddressSummary { String getCity(); } }
在方法調用中,address
獲取目標實例的屬性,並依次將其包裝到投影代理中。
已關閉的投影
其訪問器方法所有匹配目標聚合的屬性的投影接口被認爲是閉合投影。下面的例子(咱們在本章前面也使用過)是一個封閉的投影:
interface NamesOnly { String getFirstname(); String getLastname(); }
若是您使用封閉投影,Spring Data能夠優化查詢執行,由於咱們知道支持投影代理所需的全部屬性。有關更多詳細信息,請參閱參考文檔中特定於模塊的部分。
打開預測
投影接口中的訪問器方法也可用於經過使用@Value
註釋來計算新值,如如下示例所示:
interface NamesOnly { @Value("#{target.firstname + ' ' + target.lastname}") String getFullName(); … }
支持投影的聚合根在target
變量中可用。投影界面使用@Value
是一個開放的投影。在這種狀況下,Spring Data不能應用查詢執行優化,由於SpEL表達式可使用聚合根的任何屬性。
使用的表達式@Value
不該該太複雜 - 你想避免在String
變量中編程。對於很是簡單的表達式,一個選項可能會採起默認方法(在Java 8中引入),如如下示例所示:
interface NamesOnly { String getFirstname(); String getLastname(); default String getFullName() { return getFirstname.concat(" ").concat(getLastname()); } }
這種方法要求您可以純粹基於投影界面上公開的其餘訪問器方法來實現邏輯。第二種更靈活的選擇是在Spring bean中實現自定義邏輯,而後從SpEL表達式中調用它,如如下示例所示:
@Component class MyBean { String getFullName(Person person) { … } } interface NamesOnly { @Value("#{@myBean.getFullName(target)}") String getFullName(); … }
注意SpEL表達式如何引用myBean
並調用getFullName(…)
方法並將投影目標做爲方法參數轉發。SpEL表達評估支持的方法也可使用方法參數,而後能夠從表達式中引用方法參數。方法參數可經過Object
名爲的數組得到args
。如下示例顯示如何從args
數組中獲取方法參數:
interface NamesOnly { @Value("#{args[0] + ' ' + target.firstname + '!'}") String getSalutation(String prefix); }
一樣,對於更復雜的表達式,您應該使用Spring bean並讓表達式調用一個方法,如前所述。
基於類別的預測(DTO)
定義投影的另外一種方法是使用值類型DTO(數據傳輸對象),它們爲應該檢索的字段保存屬性。這些DTO類型的使用方式與投影界面的使用方式徹底相同,只是沒有代理髮生,也不能應用嵌套投影。
若是商店經過限制要加載的字段來優化查詢執行,則要從所公開的構造函數的參數名稱中肯定要加載的字段。
如下示例顯示了投影DTO:
class NamesOnly { private final String firstname, lastname; NamesOnly(String firstname, String lastname) { this.firstname = firstname; this.lastname = lastname; } String getFirstname() { return this.firstname; } String getLastname() { return this.lastname; } // equals(…) and hashCode() implementations }
避免投影DTO的樣板代碼
你能夠經過使用Project Lombok來顯着簡化DTO的代碼,該項目提供了一個 @Value class NamesOnly { String firstname, lastname; }
字段 |
動態投影
到目前爲止,咱們已經使用投影類型做爲集合的返回類型或元素類型。可是,您可能須要選擇在調用時使用的類型(這會使其變爲動態)。要應用動態投影,請使用查詢方法,以下例所示:
interface PersonRepository extends Repository<Person, UUID> { <T> Collection<T> findByLastname(String lastname, Class<T> type); }
這樣,可使用該方法按原樣或使用投影來獲取聚合,如如下示例所示:
void someMethod(PersonRepository people) { Collection<Person> aggregates = people.findByLastname("Matthews", Person.class); Collection<NamesOnly> aggregates = people.findByLastname("Matthews", NamesOnly.class); }
3.4。存儲過程
JPA 2.1規範引入了使用JPA標準查詢API調用存儲過程的支持。咱們引入了@Procedure
在存儲庫方法中聲明存儲過程元數據的註釋。
下面的示例使用如下過程:
plus1inout
HSQL DB 中的過程定義。
/; DROP procedure IF EXISTS plus1inout /; CREATE procedure plus1inout (IN arg int, OUT res int) BEGIN ATOMIC set res = arg + 1; END /;
存儲過程的元數據能夠經過NamedStoredProcedureQuery
在實體類型上使用註釋來配置。
@Entity @NamedStoredProcedureQuery(name = "User.plus1", procedureName = "plus1inout", parameters = { @StoredProcedureParameter(mode = ParameterMode.IN, name = "arg", type = Integer.class), @StoredProcedureParameter(mode = ParameterMode.OUT, name = "res", type = Integer.class) }) public class User {}
您能夠經過多種方式從存儲庫方法中引用存儲過程。要調用的存儲過程既可使用註釋的value
or procedureName
屬性直接定義,也可使用該屬性@Procedure
間接定義name
。若是沒有配置名稱,則使用存儲庫方法的名稱做爲回退。
如下示例顯示如何引用明確映射的過程:
@Procedure("plus1inout") Integer explicitlyNamedPlus1inout(Integer arg);
如下示例顯示如何使用procedureName
別名引用隱式映射的過程:
procedureName
別名在數據庫中引用名爲「plus1inout」的隱式映射過程。
@Procedure(procedureName = "plus1inout") Integer plus1inout(Integer arg);
如下示例顯示如何在如下位置引用顯式映射的命名過程EntityManager
:
EntityManager
。
@Procedure(name = "User.plus1IO") Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg);
如下示例顯示如何EntityManager
使用方法名稱引用隱式命名的存儲過程:
EntityManager
經過使用方法名稱引用隱式映射的命名存儲過程「User.plus1」 。
@Procedure Integer plus1(@Param("arg") Integer arg);
3.5。產品規格
JPA 2引入了可用於以編程方式構建查詢的標準API。經過編寫一個criteria
,您能夠爲域類定義查詢的where子句。再回過頭來看,這些標準能夠被看做是由JPA標準API約束描述的實體的謂詞。
Spring Data JPA採用Eric Evans的書「Domain Driven Design」中的規範概念,遵循相同的語義,並提供API來定義JPA標準API的這些規範。爲了支持規範,您可使用JpaSpecificationExecutor
接口擴展存儲庫接口,以下所示:
public interface CustomerRepository extends CrudRepository<Customer, Long>, JpaSpecificationExecutor { … }
額外的接口有方法讓您以各類方式執行規範。例如,該findAll
方法返回全部符合規範的實體,如如下示例所示:
List<T> findAll(Specification<T> spec);
的Specification
接口被定義爲以下:
public interface Specification<T> { Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder); }
可使用規範輕鬆地在實體上構建一組可擴展的謂詞,而後能夠將它們組合使用,JpaRepository
而無需爲每一個須要的組合聲明查詢(方法),如如下示例所示:
public class CustomerSpecs { public static Specification<Customer> isLongTermCustomer() { return new Specification<Customer>() { public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder builder) { LocalDate date = new LocalDate().minusYears(2); return builder.lessThan(root.get(_Customer.createdAt), date); } }; } public static Specification<Customer> hasSalesOfMoreThan(MontaryAmount value) { return new Specification<Customer>() { public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) { // build query here } }; } }
誠然,樣板的數量有待改進(Java 8關閉可能最終會減小這些空間),但客戶端變得更好,您將在本節後面看到。該_Customer
類型是使用JPA Metamodel生成器生成的元模型類型(例如,請參閱Hibernate實現的文檔)。因此表達式,_Customer.createdAt
假定Customer
有一個createdAt
類型的屬性Date
。除此以外,咱們已經在業務需求抽象層次上表達了一些標準並建立了可執行文件Specifications
。因此客戶可能會使用a Specification
以下:
List<Customer> customers = customerRepository.findAll(isLongTermCustomer());
爲何不爲這種數據訪問建立查詢?使用單個Specification
查詢聲明不會得到比普通查詢聲明更多的好處。規範的力量真正發揮出來,當你把它們組合起來創造新的Specification
物體。您能夠經過Specifications
咱們提供的幫助器類來實現此目的,以構建相似於如下內容的表達式:
MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR); List<Customer> customers = customerRepository.findAll( where(isLongTermCustomer()).or(hasSalesOfMoreThan(amount)));
Specifications
提供了一些「膠合代碼」方法來連接和組合Specification
實例。這些方法容許您經過建立新的Specification
實現並將它們與現有的實現相結合來擴展數據訪問層。
3.6。按實例查詢
3.6.1。介紹
本章提供了對示例查詢的介紹,並說明了如何使用它。
按實例查詢(QBE)是一種用戶界面友好的查詢技術。它容許動態查詢建立,而且不要求您編寫包含字段名稱的查詢。事實上,按示例查詢不須要您經過使用特定於商店的查詢語言編寫查詢。
3.6.2。用法
按示例查詢API由三部分組成:
-
探測器:具備填充字段的域對象的實際示例。
-
ExampleMatcher
:提供ExampleMatcher
有關如何匹配特定字段的詳細信息。它能夠在多個示例中重用。 -
Example
:一個Example
由探針和ExampleMatcher
。它用於建立查詢。
按實例查詢很是適合多種用例:
-
用一組靜態或動態約束查詢數據存儲。
-
頻繁重構域對象,而不用擔憂打破現有查詢。
-
獨立於底層數據存儲API工做。
按示例查詢也有一些限制:
-
不支持嵌套或分組屬性約束,例如
firstname = ?0 or (firstname = ?1 and lastname = ?2)
。 -
僅支持字符串的開始/包含/結束/正則表達式匹配以及其餘屬性類型的精確匹配。
在開始使用示例查詢以前,您須要有一個域對象。要開始,請爲您的存儲庫建立一個接口,如如下示例所示:
public class Person { @Id private String id; private String firstname; private String lastname; private Address address; // … getters and setters omitted }
上例顯示了一個簡單的域對象。你能夠用它來建立一個Example
。默認狀況下,具備null
值的字段將被忽略,而且字符串將經過使用特定於商店的默認值進行匹配。示例能夠經過使用of
工廠方法或使用來構建ExampleMatcher
。Example
是不可變的。如下清單顯示了一個簡單的示例:
Person person = new Person(); person.setFirstname("Dave"); Example<Person> example = Example.of(person);
建立一個域對象的新實例。 | |
設置要查詢的屬性。 | |
建立Example 。 |
理想狀況下,示例將使用存儲庫執行。爲此,請讓您的存儲庫接口擴展QueryByExampleExecutor<T>
。如下清單顯示了QueryByExampleExecutor
界面的摘錄:
QueryByExampleExecutor
public interface QueryByExampleExecutor<T> { <S extends T> S findOne(Example<S> example); <S extends T> Iterable<S> findAll(Example<S> example); // … more functionality omitted. }
3.6.3。示例匹配器
示例不限於默認設置。您能夠經過使用ExampleMatcher
,爲字符串匹配,空值處理和特定於屬性的設置指定您本身的缺省值,如如下示例所示:
Person person = new Person(); person.setFirstname("Dave"); ExampleMatcher matcher = ExampleMatcher.matching() .withIgnorePaths("lastname") .withIncludeNullValues() .withStringMatcherEnding(); Example<Person> example = Example.of(person, matcher);
建立一個域對象的新實例。 | |
設置屬性。 | |
建立一個ExampleMatcher 指望全部值匹配的值。即便沒有進一步的配置,它也能夠在此階段使用。 |
|
構建一個新的ExampleMatcher 來忽略lastname 屬性路徑。 |
|
構造一個新的ExampleMatcher 來忽略lastname 屬性路徑幷包含空值。 |
|
構造一個新的ExampleMatcher 來忽略lastname 屬性路徑,包含空值,並執行後綴字符串匹配。 |
|
Example 根據域對象和配置建立一個新的ExampleMatcher 。 |
默認狀況下,ExampleMatcher
指望探針上設置的全部值匹配。若是你想獲得匹配隱式定義的任何謂詞的結果,請使用ExampleMatcher.matchingAny()
。
您能夠爲單個屬性指定行爲(例如「firstname」和「lastname」,或者對於嵌套屬性,「address.city」)。您可使用匹配選項和區分大小寫來調整它,如如下示例所示:
ExampleMatcher matcher = ExampleMatcher.matching() .withMatcher("firstname", endsWith()) .withMatcher("lastname", startsWith().ignoreCase()); }
另外一種配置匹配器選項的方法是使用lambdas(在Java 8中引入)。這種方法建立了一個回調,要求實現者修改匹配器。您無需返回匹配器,由於配置選項在匹配器實例中保存。如下示例顯示使用lambdas的匹配器:
ExampleMatcher matcher = ExampleMatcher.matching() .withMatcher("firstname", match -> match.endsWith()) .withMatcher("firstname", match -> match.startsWith()); }
經過Example
使用配置的合併視圖建立的查詢。默認匹配設置能夠在ExampleMatcher
級別上設置,而單獨的設置能夠應用於特定的屬性路徑。設置的設置ExampleMatcher
將由屬性路徑設置繼承,除非它們是明肯定義的。屬性修補程序上的設置優先於默認設置。下表介紹了各類ExampleMatcher
設置的範圍:
設置 | 範圍 |
---|---|
空操做 |
|
字符串匹配 |
|
忽略屬性 |
屬性路徑 |
區分大小寫 |
|
價值轉化 |
屬性路徑 |
3.6.4。執行一個例子
在Spring Data JPA中,您能夠將示例查詢與存儲庫一塊兒使用,如如下示例所示:
public interface PersonRepository extends JpaRepository<Person, String> { … } public class PersonService { @Autowired PersonRepository personRepository; public List<Person> findPeople(Person probe) { return personRepository.findAll(Example.of(probe)); } }
目前,只有SingularAttribute 屬性能夠用於屬性匹配。 |
屬性說明符接受屬性名稱(如firstname
和lastname
)。您能夠經過連接屬性與點(address.city
)進行導航。您還可使用匹配選項和區分大小寫來調整它。
下表顯示了StringMatcher
您可使用的各類選項以及在名爲的字段上使用它們的結果firstname
:
匹配 | 邏輯結果 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3.7。事務性
默認狀況下,存儲庫實例上的CRUD方法是事務性的。對於讀操做,事務配置readOnly
標誌設置爲true
。全部其餘人都配置爲純文本,@Transactional
以便應用默認事務配置。有關詳細信息,請參閱JavaDoc SimpleJpaRepository
。若是您須要調整存儲庫中聲明的某個方法的事務配置,請在存儲庫接口中從新聲明該方法,以下所示:
public interface UserRepository extends CrudRepository<User, Long> { @Override @Transactional(timeout = 10) public List<User> findAll(); // Further query method declarations }
這樣作會致使findAll()
方法以10秒的超時運行而且沒有readOnly
標誌。
另外一種改變事務行爲的方式是使用一個(一般)覆蓋多個存儲庫的外觀或服務實現。其目的是爲非CRUD操做定義事務邊界。如下示例顯示如何爲多個存儲庫使用這種外觀:
@Service class UserManagementImpl implements UserManagement { private final UserRepository userRepository; private final RoleRepository roleRepository; @Autowired public UserManagementImpl(UserRepository userRepository, RoleRepository roleRepository) { this.userRepository = userRepository; this.roleRepository = roleRepository; } @Transactional public void addRoleToAllUsers(String roleName) { Role role = roleRepository.findByName(roleName); for (User user : userRepository.findAll()) { user.addRole(role); userRepository.save(user); } }
這個例子致使調用addRoleToAllUsers(…)
在一個事務內部運行(參與現有的或建立一個新的,若是沒有運行的話)。而後忽略存儲庫中的事務配置,由於外部事務配置肯定了實際使用的配置。請注意,您必須激活<tx:annotation-driven />
或@EnableTransactionManagement
顯式使用才能得到基於註釋的外牆配置。本示例假定您使用組件掃描。
3.7.1。事務查詢方法
要讓您的查詢方法成爲事務性的,請@Transactional
在您定義的存儲庫接口上使用,如如下示例所示:
@Transactional(readOnly = true) public interface UserRepository extends JpaRepository<User, Long> { List<User> findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void deleteInactiveUsers(); }
一般狀況下,您但願readOnly
標誌被設置爲true
,由於大多數查詢方法只能讀取數據。與此相反,deleteInactiveUsers()
使用@Modifying
註釋並覆蓋事務配置。所以,該方法運行readOnly
標誌設置爲false
。
您能夠將事務用於只讀查詢,並經過設置 |
3.8。鎖定
要指定要使用的鎖定模式,能夠@Lock
在查詢方法上使用註釋,如如下示例所示:
interface UserRepository extends Repository<User, Long> { // Plain query method @Lock(LockModeType.READ) List<User> findByLastname(String lastname); }
這個方法聲明將致使觸發查詢中配備了LockModeType
的READ
。您還能夠經過在存儲庫接口中從新聲明它們並添加@Lock
註釋來爲CRUD方法定義鎖定,如如下示例所示:
interface UserRepository extends Repository<User, Long> { // Redeclaration of a CRUD method @Lock(LockModeType.READ); List<User> findAll(); }
3.9。審計
3.9.1。基本
Spring Data提供複雜的支持以透明地跟蹤誰建立或更改實體以及發生更改的時間。爲了從該功能中受益,您必須爲實體類配備審計元數據,這些元數據可使用註釋或經過實現接口來定義。
基於註釋的審計元數據
咱們提供@CreatedBy
和@LastModifiedBy
捕捉誰建立或修改的實體以及用戶@CreatedDate
和@LastModifiedDate
捕捉時的變化發生了。
class Customer { @CreatedBy private User user; @CreatedDate private DateTime createdDate; // … further properties omitted }
正如您所看到的,註釋能夠選擇性地應用,具體取決於您要捕獲的信息。更改時間捕捉註釋能夠在類型喬達,時間,性質使用DateTime
,遺留Java Date
和Calendar
,JDK8日期和時間類型,以及long
或Long
。
基於接口的審計元數據
若是您不想使用註釋來定義審計元數據,則可讓您的域類實現該Auditable
界面。它公開了全部審計屬性的setter方法。
還有一個便利的基類,AbstractAuditable
您能夠擴展該基類以免須要手動實現接口方法。這樣作會增長域類與Spring Data的耦合,這多是您想要避免的。一般,定義審計元數據的基於註釋的方式是首選,由於它侵入性更小,更靈活。
AuditorAware
若是您使用@CreatedBy
或者@LastModifiedBy
,審計基礎設施須要知道當前的委託人。爲此,咱們提供一個AuditorAware<T>
SPI接口,您必須實施該接口,以告知基礎架構當前用戶或系統與應用程序進行交互的人員。泛型T
定義了哪些類型的屬性註釋@CreatedBy
或@LastModifiedBy
必須是。
如下示例顯示了使用Spring Security Authentication
對象的接口的實現:
class SpringSecurityAuditorAware implements AuditorAware<User> { public User getCurrentAuditor() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null || !authentication.isAuthenticated()) { return null; } return ((MyUserDetails) authentication.getPrincipal()).getUser(); } }
該實現訪問Authentication
Spring Security提供的對象,並查找UserDetails
您在UserDetailsService
實現中建立的自定義實例。咱們在這裏假設你正在經過UserDetails
實現暴露域用戶,可是根據Authentication
找到的,你也能夠從任何地方查看它。:leveloffset:-1
3.9.2。JPA審計
通常審計配置
Spring Data JPA附帶一個實體監聽器,可用於觸發捕獲審計信息。首先,您必須AuditingEntityListener
在orm.xml
文件內的持久性上下文中註冊要用於全部實體的對象,如如下示例所示:
<persistence-unit-metadata> <persistence-unit-defaults> <entity-listeners> <entity-listener class="….data.jpa.domain.support.AuditingEntityListener" /> </entity-listeners> </persistence-unit-defaults> </persistence-unit-metadata>
您還能夠AuditingEntityListener
使用@EntityListeners
註釋在每一個實體的基礎上啓用,以下所示:
@Entity @EntityListeners(AuditingEntityListener.class) public class MyEntity { }
審計功能須要spring-aspects.jar 位於類路徑中。 |
經過orm.xml
適當修改並spring-aspects.jar
在類路徑中,激活審計功能就是將Spring Data JPA auditing
命名空間元素添加到您的配置中,以下所示:
<jpa:auditing auditor-aware-ref="yourAuditorAwareBean" />
從Spring Data JPA 1.5開始,您能夠經過使用註釋註釋配置類來啓用審計@EnableJpaAuditing
。您仍然必須修改該orm.xml
文件並spring-aspects.jar
在類路徑中進行。如下示例顯示如何使用@EnableJpaAuditing
註釋:
@Configuration @EnableJpaAuditing class Config { @Bean public AuditorAware<AuditableUser> auditorProvider() { return new AuditorAwareImpl(); } }
若是您AuditorAware
向該類型公開了一個類型的bean ApplicationContext
,則審計基礎結構會自動將其選中並使用它來肯定要在域類型上設置的當前用戶。若是您有多個註冊的實現ApplicationContext
,您能夠經過顯式設置auditorAwareRef
屬性來選擇要使用的實現@EnableJpaAuditing
。
3.10。其餘注意事項
3.10.1。使用JpaContext
在自定義實現
在處理多個EntityManager
實例和自定義存儲庫實現時,您須要將正確的鏈接EntityManager
到存儲庫實現類。您能夠經過EntityManager
在@PersistenceContext
註釋中明確命名或者若是EntityManager
是@Autowired
,經過使用來實現@Qualifier
。
從Spring Data JPA 1.9開始,Spring Data JPA包含一個名爲的類JpaContext
,它容許您EntityManager
經過託管域類獲取,假設它只由EntityManager
應用程序中的一個實例管理。如下示例顯示如何JpaContext
在自定義存儲庫中使用:
JpaContext
在定製存儲庫實現中使用
class UserRepositoryImpl implements UserRepositoryCustom { private final EntityManager em; @Autowired public UserRepositoryImpl(JpaContext context) { this.em = context.getEntityManagerByManagedType(User.class); } … }
這種方法的優勢是,若是域類型被分配給不一樣的持久性單元,則沒必要觸摸存儲庫來改變對持久性單元的引用。
3.10.2。合併持久性單元
Spring支持多個持久化單元。然而,有時候,您可能但願模塊化應用程序,但仍要確保全部這些模塊都在單個持久性單元中運行。爲了實現這種行爲,Spring Data JPA提供了一個PersistenceUnitManager
基於名稱自動合併持久性單元的實現,以下例所示:
<bean class="….LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitManager"> <bean class="….MergingPersistenceUnitManager" /> </property> </bean>
類路徑掃描@Entity類和JPA映射文件
簡單的JPA設置須要列出全部註解映射的實體類orm.xml
。這一樣適用於XML映射文件。Spring Data JPA提供了一個ClasspathScanningPersistenceUnitPostProcessor
能夠獲取配置的基礎包並可選擇使用映射文件名模式。而後,它會掃描給定的包中用@Entity
or 註釋的類@MappedSuperclass
,並加載與文件名模式匹配的配置文件,並將它們傳遞給JPA配置。後處理器必須配置以下:
<bean class="….LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitPostProcessors"> <list> <bean class="org.springframework.data.jpa.support.ClasspathScanningPersistenceUnitPostProcessor"> <constructor-arg value="com.acme.domain" /> <property name="mappingFileNamePattern" value="**/*Mapping.xml" /> </bean> </list> </property> </bean>
從Spring 3.1開始,可直接配置要掃描的軟件包LocalContainerEntityManagerFactoryBean 以啓用實體類的類路徑掃描。有關詳細信息,請參閱JavaDoc。 |
3.10.3。CDI集成
存儲庫接口的實例一般由容器建立,對於Spring而言,在使用Spring Data時,Spring是最天然的選擇。Spring爲建立bean實例提供了複雜的支持,如建立存儲庫實例中所述。從版本1.1.0開始,Spring Data JPA附帶一個自定義CDI擴展,容許在CDI環境中使用存儲庫抽象。該擴展是JAR的一部分。要激活它,請在您的類路徑中包含Spring Data JPA JAR。
您如今能夠經過爲EntityManagerFactory
and 執行CDI Producer來設置基礎架構EntityManager
,如如下示例所示:
class EntityManagerFactoryProducer { @Produces @ApplicationScoped public EntityManagerFactory createEntityManagerFactory() { return Persistence.createEntityManagerFactory("my-presistence-unit"); } public void close(@Disposes EntityManagerFactory entityManagerFactory) { entityManagerFactory.close(); } @Produces @RequestScoped public EntityManager createEntityManager(EntityManagerFactory entityManagerFactory) { return entityManagerFactory.createEntityManager(); } public void close(@Disposes EntityManager entityManager) { entityManager.close(); } }
必要的設置可能因JavaEE環境而異。你可能只須要從新聲明EntityManager
一個CDI bean就能夠了,以下所示:
class CdiConfig { @Produces @RequestScoped @PersistenceContext public EntityManager entityManager; }
在前面的示例中,容器必須可以建立JPA EntityManagers
自己。全部配置都會將JPA從新導出EntityManager
爲CDI bean。
Spring Data JPA CDI擴展將全部可用的EntityManager
實例選爲CDI bean,並在容器請求存儲庫類型的bean時爲Spring Data存儲庫建立代理。所以,獲取Spring Data存儲庫的一個實例是聲明一個@Injected
屬性的問題,以下例所示:
class RepositoryClient { @Inject PersonRepository repository; public void businessMethod() { List<Person> people = repository.findAll(); } }
4.附錄
附錄A:命名空間參考
該<repositories />
元素
該<repositories />
元素觸發了Spring Data存儲庫基礎設施的設置。最重要的屬性是base-package
,它定義了用於掃描Spring數據存儲庫接口的包。請參閱「 XML配置 」。下表描述了該<repositories />
元素的屬性:
名稱 | 描述 |
---|---|
|
定義要 |
|
定義後綴以自動檢測自定義存儲庫實現。名稱以配置的後綴結尾的類被視爲候選。默認爲 |
|
肯定用於建立查找器查詢的策略。有關詳細信息,請參閱「 查詢查找策略 」。默認爲 |
|
定義搜索包含外部定義的查詢的屬性文件的位置。 |
|
是否應該考慮嵌套的存儲庫接口定義。默認爲 |
附錄B:Poppers命名空間參考
<populator />元素
該<populator />
元素容許經過Spring Data存儲庫基礎結構填充數據存儲。[ 1 ]
名稱 | 描述 |
---|---|
|
在哪裏能夠找到要從存儲庫中讀取對象的文件。 |
附錄C:存儲庫查詢關鍵字
支持的查詢關鍵字
下表列出了Spring Data存儲庫查詢派生機制一般支持的關鍵字。可是,請參閱特定商店的文檔以獲取支持的關鍵字的確切列表,由於此處列出的某些關鍵字可能在特定商店中不受支持。
邏輯關鍵字 | 關鍵字表達 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
附錄D:存儲庫查詢返回類型
支持的查詢返回類型
下表列出了Spring Data存儲庫一般支持的返回類型。可是,請參閱特定於商店的文檔以獲取受支持的返回類型的確切列表,由於此處列出的某些類型可能在特定商店中不受支持。
地理空間類型(如GeoResult ,GeoResults ,和GeoPage )是僅對支持地理空間查詢數據存儲可用。 |
返回類型 | 描述 |
---|---|
|
表示沒有返回值。 |
基元 |
Java基元。 |
包裝類型 |
Java包裝類型。 |
|
一個獨特的實體。指望查詢方法最多返回一個結果。若是沒有找到結果, |
|
一個 |
|
一 |
|
一 |
|
Java 8或番石榴 |
|
Scala或Javalang |
|
Java 8 |
|
一 |
|
Java 8 |
|
一 |
|
大小的數據塊,指示是否有更多數據可用。須要一個 |
|
A |
|
帶有附加信息的結果輸入,例如到參考位置的距離。 |
|
|
|
甲 |
|
|
|
項目反應堆 |
|
RxJava |
|
一個RxJava |
|
一個RxJava |
附錄E:常見問題
共同
-
我想得到關於哪些方法在內部調用的更詳細的日誌記錄信息
JpaRepository
(例如)。我如何得到它們?您可使用
CustomizableTraceInterceptor
Spring提供的,如如下示例所示:<bean id="customizableTraceInterceptor" class=" org.springframework.aop.interceptor.CustomizableTraceInterceptor"> <property name="enterMessage" value="Entering $[methodName]($[arguments])"/> <property name="exitMessage" value="Leaving $[methodName](): $[returnValue]"/> </bean> <aop:config> <aop:advisor advice-ref="customizableTraceInterceptor" pointcut="execution(public * org.springframework.data.jpa.repository.JpaRepository+.*(..))"/> </aop:config>
基礎設施
-
目前我已經實現了基於的存儲庫層
HibernateDaoSupport
。我SessionFactory
使用Spring 建立了一個AnnotationSessionFactoryBean
。我如何讓Spring Data存儲庫在這個環境中工做?必須更換
AnnotationSessionFactoryBean
與HibernateJpaSessionFactoryBean
以下:例111.SessionFactory
從a中查找aHibernateEntityManagerFactory
<bean id="sessionFactory" class="org.springframework.orm.jpa.vendor.HibernateJpaSessionFactoryBean"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean>
審計
-
我想使用Spring Data JPA審計功能,可是個人數據庫已經配置爲在實體上設置修改和建立日期。如何防止Spring Data以編程方式設置日期。
將名稱空間元素的
set-dates
屬性設置auditing
爲false
。
附錄F:術語表
- AOP
-
面向方面的編程
- Commons DBCP
-
Commons DataBase鏈接池 - 來自Apache基礎的庫,提供DataSource接口的池實現。
- CRUD
-
建立,讀取,更新,刪除 - 基本的持久性操做。
- DAO
-
數據訪問對象 - 從持久化對象中分離持久邏輯的模式
- 依賴注入
-
從外部將組件的依賴關係傳遞給組件的模式,釋放組件以查找依賴關係自己。有關更多信息,請參閱http://en.wikipedia.org/wiki/Dependency_Injection。
- 的EclipseLink
-
實現JPA的對象關係映射器 - http://www.eclipselink.org
- 過冬
-
實現JPA的對象關係映射器 - http://www.hibernate.org
- JPA
-
Java持久性API
- 彈簧
-
Java應用程序框架 - http://projects.spring.io/spring-framework