spring-data-jpa官方文檔

©2008-2016原做者。javascript

  本文件副本可供您自行使用並分發給其餘人,前提是您不收取任何此類副本的費用,並進一步規定每份副本均包含此版權聲明,不管是以印刷版仍是電子版分發。
目錄

前言

Spring Data JPA爲Java持久性API(JPA)提供存儲庫支持。它簡化了須要訪問JPA數據源的應用程序的開發。

項目元數據

在使用Spring Data JPA時,您可能會發現如下連接有幫助:html

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增長了如下功能:

  • 支持存儲庫查詢方法中的投影

  • 經過示例支持查詢

  • 如下註解已啓用的基礎上組成的註釋:@EntityGraph@Lock@Modifying@Query@QueryHints,和@Procedure

  • 支持Contains集合表達式關鍵字。

  • AttributeConverter爲實現ZoneIdJSR-310和ThreeTenBP的。

  • 升級到Querydsl 4,Hibernate 5,OpenJPA 2.4和EclipseLink 2.6.1。=依賴性

因爲各個Spring Data模塊的初始日期不一樣,它們中的大多數都帶有不一樣的主版本號和次版本號。尋找兼容版本的最簡單方法是依靠咱們隨定義的兼容版本提供的Spring Data Release BOM。在Maven項目中,您將在<dependencyManagement />POM 部分聲明這種依賴關係,以下所示:

示例1.使用Spring Data發行版BOM
<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:當前快照

  • M1M2等等:里程碑

  • RC1RC2等等:發佈候選人

  • RELEASE:GA版本

  • SR1SR2等等:服務版本

在咱們的Spring Data示例存儲庫中能夠找到使用BOM的一個工做示例有了這個,你能夠在塊中聲明你想使用的Spring數據模塊而不須要版本<dependencies />,以下所示:

例2.聲明一個依賴到Spring Data模塊
<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數據存儲庫文檔和你的模塊

本章介紹Spring Data存儲庫的核心概念和接口。本章中的信息來自Spring Data Commons模塊。它使用Java持久性API(JPA)模塊的配置和代碼示例。您應該將XML名稱空間聲明和要擴展的類型調整爲您使用的特定模塊的等同項。「 命名空間參考 」涵蓋了全部支持存儲庫API的Spring Data模塊支持的XML配置。「 存儲庫查詢關鍵字 」通常涵蓋了存儲庫抽象支持的查詢方法關鍵字。有關模塊特定功能的詳細信息,請參閱本文檔的該模塊章節。

2.1。核心概念

Spring Data存儲庫抽象中的中心接口是Repository它須要管理域類以及域類的ID類型做爲類型參數。該接口主要做爲標記接口來捕獲要使用的類型,並幫助您發現擴展該接口的接口。CrudRepository規定對於正在管理的實體類複雜的CRUD功能。

例3.  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的實體。
  咱們還提供持久性技術特定抽象,如JpaRepositoryMongoRepository這些接口擴展CrudRepository並公開了持久化技術的基本功能,以及至關通用的持久化技術無關接口,例如CrudRepository

除此以外CrudRepository,還有一個PagingAndSortingRepository抽象增長了其餘方法來簡化對實體的分頁訪問:

示例4.  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查詢的查詢派生都是可用的。如下列表顯示派生計數查詢的接口定義:

示例5.派生計數查詢
interface UserRepository extends CrudRepository<User, Long> { long countByLastname(String lastname); }

如下列表顯示派生刪除查詢的接口定義:

示例6.派生刪除查詢
interface UserRepository extends CrudRepository<User, Long> { long deleteByLastname(String lastname); List<User> removeByLastname(String lastname); }

2.2。查詢方法

標準CRUD功能存儲庫一般會在底層數據存儲上進行查詢。使用Spring Data,聲明這些查詢變成了一個四步過程:

  1. 聲明一個擴展Repository或其子接口的接口,並將其鍵入它應該處理的域類和ID類型,如如下示例所示:

    interface PersonRepository extends Repository<Person, Long> {  }
  2. 在接口上聲明查詢方法。

    interface PersonRepository extends Repository<Person, Long> { List<Person> findByLastname(String lastname); }
  3. 設置Spring以使用JavaConfigXML配置爲這些接口建立代理實例

    1. 要使用Java配置,請建立相似於如下的類:

      import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @EnableJpaRepositories class Config {}
    2. 要使用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註釋的一個屬性

  4. 注入資源庫實例並使用它,如如下示例所示:

    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。微調儲存庫定義

一般狀況下,你的資料庫接口擴展RepositoryCrudRepositoryPagingAndSortingRepository或者,若是您不想擴展Spring Data接口,也可使用註釋庫接口進行註釋@RepositoryDefinition擴展CrudRepository公開了一套完整的方法來操縱你的實體。若是您想選擇暴露的方法,請將要公開的方法複製CrudRepository到您的域存儲庫中。

  這樣作可讓您在提供的Spring Data Repositories功能之上定義本身的抽象。

如下示例顯示如何選擇性地公開CRUD方法(findById以及save在這種狀況下):

示例7.選擇性地暴露CRUD方法
@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元註釋讓工具供應商(如IDEAEclipseKotlin)以通用方式提供空安全支持,而無需爲Spring註釋提供硬編碼支持。要啓用對查詢方法的可空約束的運行時檢查,您須要使用Spring的@NonNullApiin 激活包級別的非可空性package-info.java,如如下示例所示:

示例8.在中聲明不可爲空  package-info.java
@org.springframework.lang.NonNullApi package com.acme;

一旦存在非空默認值,存儲庫查詢方法調用就會在運行時驗證可空性約束。若是查詢執行結果違反了定義的約束,則會引起異常。當方法返回時,會發生這種狀況,null但聲明爲非空(存儲庫所在的包上定義了註釋的默認值)。若是您想再次選擇可空結果,請選擇性地使用@Nullable單個方法。使用本節開始提到的結果包裝類型將繼續按預期工做:將空結果轉換爲表示不存在的值。

如下示例顯示了剛剛描述的許多技術:

例子9.使用不一樣的可空性約束
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當執行的查詢不產生結果時拋出一個IllegalArgumentExceptionemailAddress交給方法拋出一個null
  null當執行的查詢不會產生結果時返回也接受null做爲的價值emailAddress
  Optional.empty()當執行的查詢不會產生結果時返回IllegalArgumentExceptionemailAddress交給方法拋出一個null
基於Kotlin的知識庫中的可空性

Kotlin定義了可用於語言的可空性約束Kotlin代碼編譯爲字節碼,該字節碼不經過方法簽名表示可空約束,而是經過編譯後的元數據表示。確保kotlin-reflect在項目中包含JAR,以便對Kotlin的可空性限制進行檢討。Spring Data存儲庫使用語言機制來定義這些約束以應用相同的運行時檢查,以下所示:

例10.在Kotlin存儲庫上使用可空性約束
interface UserRepository : Repository<User, String> { fun findByUsername(username: String): User  fun findByFirstname(firstname: String?): User?  }
  該方法將參數和結果定義爲不可空(Kotlin默認值)。Kotlin編譯器拒絕傳遞null給方法的方法調用若是查詢執行產生空結果,EmptyResultDataAccessException則拋出。
  該方法接受nullfirstname參數,並返回null,若是查詢執行不產生結果。

2.3.3。將存儲庫與多個Spring數據模塊一塊兒使用

在應用程序中使用獨特的Spring Data模塊使事情變得簡單,由於定義範圍中的全部存儲庫接口都綁定到Spring Data模塊。有時,應用程序須要使用多個Spring Data模塊。在這種狀況下,存儲庫定義必須區分持久性技術。當它在類路徑中檢測到多個存儲庫工廠時,Spring Data會進入嚴格的存儲庫配置模式。嚴格配置使用存儲庫或域類上的詳細信息來決定存儲庫定義的Spring Data模塊綁定:

  1. 若是存儲庫定義擴展了特定於模塊的存儲庫,那麼它是特定Spring Data模塊的有效候選者。

  2. 若是域類使用特定於模塊的類型註釋進行註釋,則它是特定的Spring Data模塊的有效候選者。Spring Data模塊接受第三方註釋(好比JPA @Entity)或者提供他們本身的註解(例如@DocumentSpring Data MongoDB和Spring Data Elasticsearch)。

如下示例顯示使用模塊特定接口(本例中爲JPA)的存儲庫:

示例11.使用模塊特定接口的存儲庫定義
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模塊的有效候選者。

如下示例顯示使用通用接口的存儲庫:

示例12.使用通用接口的存儲庫定義
interface AmbiguousRepository extends Repository<User, Long> {  } @NoRepositoryBean interface MyBaseRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {  } interface AmbiguousUserRepository extends MyBaseRepository<User, Long> {  }

AmbiguousRepositoryAmbiguousUserRepository僅延伸Repository,並CrudRepository在他們的類型層次。雖然在使用獨特的Spring Data模塊時這很是好,但多個模塊沒法區分這些存儲庫應綁定到哪一個特定的Spring Data。

如下示例顯示了使用帶註釋的域類的存儲庫:

示例13.使用帶註釋的域類的存儲庫定義
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註解註釋的。

如下錯誤示例顯示了使用具備混合註釋的域類的存儲庫:

示例14.使用具備混合註釋的域類的存儲庫定義
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的配置中基礎包是強制性的。

如下示例顯示基本包的註釋驅動配置:

示例15.基礎包的註釋驅動配置
@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(默認)組合CREATEUSE_DECLARED_QUERY它首先查找已聲明的查詢,而且若是未找到已聲明的查詢,則會建立一個自定義的基於方法名稱的查詢。這是默認的查找策略,所以,若是您沒有明確配置任何內容,就會使用它。它容許經過方法名稱進行快速查詢定義,還能夠根據須要引入已聲明的查詢來自定義這些查詢。

2.4.2。查詢建立

構建到Spring Data存儲庫基礎結構中的查詢構建器機制對構建存儲庫實體上的約束查詢很是有用。該機制條前綴find…Byread…Byquery…Bycount…By,和get…By從所述方法和開始分析它的其他部分。引入子句能夠包含更多的表達式,例如Distinct在要建立的查詢上設置不一樣的標誌。可是,第一個By用做分隔符以指示實際標準的開始。在很是基本的層面上,您能夠定義實體屬性的條件並將它們與And鏈接起來Or如下示例顯示如何建立多個查詢:

示例16.從方法名稱建立查詢
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); }

解析方法的實際結果取決於您爲其建立查詢的持久性存儲。可是,有一些通常事情須要注意:

  • 表達式一般是屬性遍歷和能夠鏈接的運算符。您可使用組合屬性表達式ANDOR您還能夠獲得這樣的運營商爲支撐BetweenLessThanGreaterThan,和Like該屬性的表達式。受支持的操做員可能因數據存儲而異,所以請參閱相應部分的參考文檔。

  • 方法解析器支持IgnoreCase爲單個屬性(例如,findByLastnameIgnoreCase(…))或支持忽略大小寫的類型的全部屬性(一般爲String實例 - 例如findByLastnameAndFirstnameAllIgnoreCase(…)設置標誌支持忽略狀況的方式可能因商店而異,所以請參閱參考文檔中的相關部分以獲取特定於商店的查詢方法。

  • 您能夠經過OrderBy向引用屬性的查詢方法附加子句並提供排序方向(AscDesc來應用靜態排序要建立支持動態排序的查詢方法,請參閱「 特殊參數處理 」。

2.4.3。屬性表達式

屬性表達式只能引用被管實體的直接屬性,如上例所示。在查詢建立時,您已經確保解析的屬性是託管域類的屬性。可是,您也能夠經過遍歷嵌套屬性來定義約束。考慮如下方法簽名:

List<Person> findByAddressZipCode(ZipCode zipCode);

假設a Person有一個Addresswith ZipCode在這種狀況下,該方法建立屬性遍歷x.address.zipCode解析算法首先將整個part(AddressZipCode)做爲屬性進行解釋,而後檢查該域名是否具備該名稱(未添加大小)。若是算法成功,則使用該屬性。若是不是的話,該算法未來自右側的駱駝案件部分的來源拆分爲頭部和尾部,並嘗試找到相應的屬性 - 在咱們的示例中AddressZipCode若是算法找到具備該頭部的屬性,它將採用尾部並從該處繼續構建樹,而後按照剛剛描述的方式分割尾部。若是第一次分割不匹配,則算法將分割點移到左側(AddressZipCode)並繼續。

雖然這應該適用於大多數狀況,但算法可能會選擇錯誤的屬性。假設這個Person類也有一個addressZip屬性。該算法將在第一輪拆分中匹配,選擇錯誤的屬性,並失敗(由於addressZip可能沒有code屬性的類型)。

爲了解決這個歧義,你能夠\_在你的方法名稱中使用手動定義遍歷點。因此咱們的方法名稱以下所示:

List<Person> findByAddress_ZipCode(ZipCode zipCode);

由於咱們將下劃線字符視爲保留字符,因此咱們強烈建議遵循如下標準Java命名約定(即,不使用屬性名稱中的下劃線,而是使用駝峯大小寫代替)。

2.4.4。特殊參數處理

要處理查詢中的參數,請定義方法參數,如前面的示例中所示。除此以外,基礎設施認可某些特定的類型,如PageableSort,動態地應用分頁和排序,以查詢。如下示例演示了這些功能:

例子17. 在查詢方法中使用 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

查詢方法的結果能夠經過使用firsttop關鍵字來限制,這些關鍵字能夠互換使用。一個可選的數值能夠附加到topfirst指定要返回的最大結果大小。若是該號碼被忽略,則假定結果大小爲1。如下示例顯示如何限制查詢大小:

示例18.使用 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數據存儲區中 - 使用特定的方法來執行流式處理,如如下示例所示:

示例19.使用Java 8對查詢的結果進行流式處理  Stream<T>
@Query("select u from User u") Stream<User> findAllByCustomQueryAndStream(); Stream<User> readAllByFirstnameNotNull(); @Query("select u from User u") Stream<User> streamAllPaged(Pageable pageable);
  Stream潛在包裝底層數據存儲專用資源,所以必須在使用以後被關閉。您能夠Stream經過使用該close()方法或使用Java 7 try-with-resources手動關閉,如如下示例所示:
示例20.使用 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爲您掃描的基本包,如如下示例所示:

示例21.經過XML啓用Spring Data存儲庫
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> <repositories base-package="com.acme.repositories" /> </beans:beans>

在前面的例子中,Spring被指示掃描com.acme.repositories及其全部子包以查找擴展接口Repository或其子接口之一。對於找到的每一個接口,基礎設施都會註冊持久性技術專用的,FactoryBean以建立處理調用查詢方法的適當代理。每一個bean都是在從接口名稱派生的bean名稱下注冊的,所以UserRepository將在其下注冊一個接口userRepositorybase-package屬性容許使用通配符,以便您能夠定義掃描軟件包的模式。

使用過濾器

默認狀況下,基礎架構將拾取擴展Repository位於已配置基礎包下的持久性技術特定子接口的每一個接口,併爲其建立一個bean實例。可是,您可能須要更細緻地控制哪些接口具備爲其建立的bean實例。要作到這一點,使用<include-filter /><exclude-filter />內部元素<repositories />的元素。語義與Spring的上下文命名空間中的元素徹底等價。有關詳細信息,請參閱這些元素Spring參考文檔

例如,要將某些接口從實例化中排除爲存儲庫Bean,可使用如下配置:

例22.使用排除過濾器元素
<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

啓用S​​pring Data存儲庫的示例配置相似於如下內容:

示例23.基於示例註釋的存儲庫配置
@Configuration @EnableJpaRepositories("com.acme.repositories") class ApplicationConfiguration { @Bean EntityManagerFactory entityManagerFactory() { // … } }
  前面的示例使用JPA特定的註釋,它將根據您實際使用的商店模塊進行更改。這一樣適用於EntityManagerFactorybean的定義請參閱包含特定於商店的配置的章節。

2.5.3。獨立使用

您還能夠在Spring容器以外使用存儲庫基礎結構 - 例如,在CDI環境中。你的類路徑中仍然須要一些Spring庫,可是,一般你也能夠經過編程來設置庫。提供存儲庫支持的Spring Data模塊提供了一個特定RepositoryFactory於您可使用的持久性技術,以下所示:

示例24.存儲庫工廠的獨立使用
RepositoryFactorySupport factory =  // Instantiate factory here UserRepository repository = factory.getRepository(UserRepository.class);

2.6。Spring數據倉庫的自定義實現

本節涵蓋存儲庫自定義以及片斷如何造成組合存儲庫。

當查詢方法須要不一樣的行爲或沒法經過查詢派生實現時,則須要提供自定義實現。Spring Data存儲庫容許您提供自定義存儲庫代碼並將其與通用CRUD抽象和查詢方法功能集成。

2.6.1。定製我的存儲庫

要使用自定義功能豐富存儲庫,必須首先爲自定義功能定義片斷接口和實現,如如下示例所示:

示例25.自定義存儲庫功能的接口
interface CustomizedUserRepository { void someCustomMethod(User user); }

而後,您可讓您的存儲庫接口另外從分段接口擴展,如如下示例所示:

示例26.實現定製存儲庫功能
class CustomizedUserRepositoryImpl implements CustomizedUserRepository { public void someCustomMethod(User user) { // Your custom implementation } }
  與片斷接口相對應的類名稱中最重要的部分是Impl後綴。

實現自己不依賴於Spring Data,能夠是普通的Spring bean。所以,您可使用標準的依賴注入行爲來向其餘bean(例如a JdbcTemplate注入引用,參與方面等等。

您可讓存儲庫接口擴展片斷接口,如如下示例所示:

示例27.對存儲庫接口的更改
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:

Example 28. Fragments with 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:

Example 29. Changes to your repository interface
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:

Example 30. Fragments overriding  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:

Example 31. Customized repository interfaces
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:

Example 32. Configuration example
<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.

Example 33. Resolution of amibiguous implementations
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:

Example 34. Manual wiring of custom implementations
<repositories base-package="com.acme.repository" /> <beans:bean id="userRepositoryImpl" class="…"> <!-- further configuration --> </beans:bean>

2.6.2. Customize the Base Repository

一節中描述的方法須要在定製基本存儲庫行爲時定製每一個存儲庫接口,以便全部存儲庫都受到影響。爲了改變全部存儲庫的行爲,能夠建立一個擴展持久性技術特定存儲庫基類的實現。而後,該類將做爲存儲庫代理的自定義基類,如如下示例所示:

示例35.定製存儲庫基類
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,如如下示例所示:

示例36.使用JavaConfig配置定製存儲庫基類
@Configuration @EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class) class ApplicationConfiguration {  }

XML名稱空間中提供了相應的屬性,如如下示例所示:

示例37.使用XML配置定製存儲庫基類
<repositories base-package="com.acme.repository" base-class="….MyRepositoryImpl" />

2.7。從聚合根發佈事件

由存儲庫管理的實體是聚合根。在Domain-Driven Design應用程序中,這些聚合根一般會發布域事件。Spring Data提供了一個註釋,稱爲@DomainEvents可用於聚合根方法的註釋,以使該出版物儘量地簡單,如如下示例所示:

例子38.從聚合根暴露域事件
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:

Example 39. QuerydslPredicateExecutor interface
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

Example 40. Querydsl integration on repositories
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配置類中使用註釋來啓用集成支持,如如下示例所示:

示例41.啓用Spring Data Web支持
@Configuration @EnableWebMvc @EnableSpringDataWebSupport class WebConfiguration {}

@EnableSpringDataWebSupport批註註冊幾個組件,咱們將在一個位討論。它也會在類路徑中檢測到Spring HATEOAS,併爲其註冊集成組件。

另外,若是你使用XML配置,註冊一個SpringDataWebConfiguration或者HateoasAwareSpringDataWebConfiguration做爲Spring bean,以下例(for SpringDataWebConfiguration)所示:

例子42.在XML中啓用Spring Data web支持
<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支持

上一節中顯示的配置註冊了一些基本組件:

DomainClassConverter

DomainClassConverter讓你在Spring MVC中的控制器方法簽名使用域類型直接,讓你不用經過儲存手動查找的狀況下,如在下面的例子:

例子43.在方法簽名中使用域類型的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註冊啓用PageableSort做爲有效的控制器方法參數,如如下示例所示:

示例44.使用Pageable做爲控制器方法參數
@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使用如下默認配置從請求參數派生實例:

表1.針對 Pageable 實例評估的請求參數

page

您想要檢索的頁面。0索引,默認爲0。

size

您要檢索的頁面大小。默認爲20。

sort

應該按格式排序的屬性property,property(,ASC|DESC)默認排序方向是升序。sort若是您想切換方向,請使用多個參數 - 例如?sort=firstname&sort=lastname,asc

要定製這種行爲,分別註冊實現PageableHandlerMethodArgumentResolverCustomizer接口或SortHandlerMethodArgumentResolverCustomizer接口的bean 它的customize()方法被調用,讓你改變設置,以下例所示:

@Bean SortHandlerMethodArgumentResolverCustomizer sortCustomizer() { return s -> s.setPropertyDelimiter("<-->"); }

若是設置現有屬性MethodArgumentResolver不足以達到您的目的,請擴展其中一個SpringDataWebConfiguration或啓用HATEOAS的等效項,覆蓋pageableResolver()sortResolver()方法,而後導入您的自定義配置文件,而不是使用@Enable註釋。

若是您須要從請求中解析多個PageableSort實例(例如,針對多個表),則可使用Spring的@Qualifier註釋區分一個。而後請求參數必須之前綴${qualifier}_如下示例顯示了生成的方法簽名:

String showUsers(Model model, @Qualifier("thing1") Pageable first, @Qualifier("thing2") Pageable second) {  }

你有填充thing1_pagething2_page等。

Pageable傳遞給方法的默認值等於a,new PageRequest(0, 20)但可使用參數@PageableDefault上的註釋進行自定義Pageable

超媒體支持Pageables

Spring HATEOAS附帶一個表示模型class(PagedResources),它容許Page使用必要的Page元數據豐富實例的內容以及讓客戶端輕鬆導航頁面的連接。將頁面轉換爲a PagedResources由Spring HATEOAS ResourceAssembler接口的實現完成,該接口稱爲PagedResourcesAssembler如下示例顯示如何使用a PagedResourcesAssembler做爲控制器方法參數:

示例45.使用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可能會prevnext鏈接鏈路,根據頁面的狀態。連接指向方法映射到的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 JsonPathXPath表達式(須要XmlBeam)),Spring數據投影(在Projections中進行了介紹)可用於綁定傳入的請求負載,如如下示例所示:

示例46.使用JSONPath或XPath表達式的HTTP負載綁定
@ProjectedPayload public interface UserPayload { @XBRead("//firstname") @JsonPath("$..firstname") String getFirstname(); @XBRead("/lastname") @JsonPath({ "$.lastname", "$.user.lastname" }) String getLastname(); }

上例中顯示的類型能夠用做Spring MVC處理方法參數,或者經過使用ParameterizedTypeReference其中一個RestTemplate方法。前面的方法聲明將嘗試firstname在給定文檔中的任何位置查找lastnameXML查詢是對輸入文檔的頂層進行。它的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"; } }
  解析查詢字符串參數匹配PredicateUser

默認綁定以下:

  • 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匹配。
  passwordPredicate解決方案中排除財產

2.8.3。存儲庫Poppop

若是您使用Spring JDBC模塊,那麼您可能很熟悉DataSource使用SQL腳本填充的支持儘管存儲庫級別沒有使用SQL做爲數據定義語言,但相似的抽象是可用的,由於它必須是獨立於存儲的。所以,populators支持XML(經過Spring的OXM抽象)和JSON(經過Jackson)來定義數據來填充存儲庫。

假設您擁有data.json包含如下內容的文件

例子47.在JSON中定義的數據
[ { "_class" : "com.acme.Person", "firstname" : "Dave", "lastname" : "Matthews" }, { "_class" : "com.acme.Person", "firstname" : "Carter", "lastname" : "Beauford" } ]

您可使用Spring Data Commons中提供的存儲庫名稱空間的populator元素來填充存儲庫。要將前面的數據填充到PersonRepository中,請聲明相似於如下內容的populator:

示例48.聲明Jackson存儲庫加載器
<?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

經過檢查\_classJSON文檔屬性來肯定JSON對象被解組的類型基礎結構最終選擇適當的存儲庫來處理反序列化的對象。

要改成使用XML定義存儲庫應該填充的數據,可使用該unmarshaller-populator元素。您將其配置爲使用Spring OXM中提供的XML編組器之一。有關詳細信息,請參閱Spring參考文檔如下示例顯示如何使用JAXB解組存儲器加載器:

例49.聲明一個反編組存儲器加載器(使用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的基礎知識:

3.1.1。Spring命名空間

Spring Data的JPA模塊包含一個容許定義存儲庫bean的自定義名稱空間。它還包含特定於JPA的某些功能和元素屬性。一般,可使用該repositories元素來設置JPA存儲庫,如如下示例所示:

示例50.使用名稱空間設置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:

Table 2. Custom JPA-specific attributes of the  repositories element

entity-manager-factory-ref

Explicitly wire the EntityManagerFactory to be used with the repositories being detected by the repositories element. Usually used if multiple EntityManagerFactory beans are used within the application. If not configured, Spring Data automatically looks up the EntityManagerFactory bean with the name entityManagerFactory in the ApplicationContext.

transaction-manager-ref

Explicitly wire the PlatformTransactionManager to be used with the repositories being detected by the repositories element. Usually only necessary if multiple transaction managers or EntityManagerFactorybeans have been configured. Default to a single defined PlatformTransactionManager inside the current ApplicationContext.

  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:

Example 51. Spring Data JPA repositories using JavaConfig
@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 implements Persistable, Spring Data JPA delegates the new detection to the isNew(…) method of the entity. See the JavaDoc for details.

  • Implementing EntityInformation: You can customize the EntityInformation abstraction used in the SimpleJpaRepositoryimplementation by creating a subclass of JpaRepositoryFactory and overriding the getEntityInformation(…) method accordingly. You then have to register the custom implementation of JpaRepositoryFactory 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.

Declared Queries

儘管獲取從方法名派生的查詢很是方便,但可能會遇到這樣的狀況,即方法名解析器不支持要使用的關鍵字,或者方法名稱會沒必要要地變得難看。因此,你能夠經過命名約定使用JPA命名查詢(請參閱使用JPA命名查詢瞭解更多信息)或至關具備註解你的查詢方法@Query(請參閱使用@Query的詳細信息)。

3.3.2。查詢建立

一般,JPA的查詢建立機制按「 查詢方法 」中的描述工做如下示例顯示了JPA查詢方法轉換爲的內容:

示例52.從方法名稱建立查詢
公共接口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 = ?2Spring Data JPA執行屬性檢查並遍歷嵌套屬性,如「 屬性表達式 」中所述。

下表描述了JPA支持的關鍵字以及包含該關鍵字的方法轉換爲:

表3.方法名稱中支持的關鍵字
關鍵詞 樣品 JPQL片斷

And

findByLastnameAndFirstname

… where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

… where x.lastname = ?1 or x.firstname = ?2

Is,Equals

findByFirstnamefindByFirstnameIsfindByFirstnameEquals

… where x.firstname = ?1

Between

findByStartDateBetween

… where x.startDate between ?1 and ?2

LessThan

findByAgeLessThan

… where x.age < ?1

LessThanEqual

findByAgeLessThanEqual

… where x.age <= ?1

GreaterThan

findByAgeGreaterThan

… where x.age > ?1

GreaterThanEqual

findByAgeGreaterThanEqual

… where x.age >= ?1

After

findByStartDateAfter

… where x.startDate > ?1

Before

findByStartDateBefore

… where x.startDate < ?1

IsNull

findByAgeIsNull

… where x.age is null

IsNotNull,NotNull

findByAge(Is)NotNull

… where x.age not null

Like

findByFirstnameLike

… where x.firstname like ?1

NotLike

findByFirstnameNotLike

… where x.firstname not like ?1

StartingWith

findByFirstnameStartingWith

… where x.firstname like ?1(參數綁定附加%

EndingWith

findByFirstnameEndingWith

… where x.firstname like ?1(參數與預先綁定%

Containing

findByFirstnameContaining

… where x.firstname like ?1(參數綁定%

OrderBy

findByAgeOrderByLastnameDesc

… where x.age = ?1 order by x.lastname desc

Not

findByLastnameNot

… where x.lastname <> ?1

In

findByAgeIn(Collection<Age> ages)

… where x.age in ?1

NotIn

findByAgeNotIn(Collection<Age> ages)

… where x.age not in ?1

True

findByActiveTrue()

… where x.active = true

False

findByActiveFalse()

… where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

… where UPPER(x.firstame) = UPPER(?1)

  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.

Example 53. XML named query configuration
<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.

Example 54. Annotation-based named query configuration
@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:

示例55. UserRepository中的查詢方法聲明
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註釋建立的查詢

例56.使用查詢方法聲明查詢  @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在查詢定義內定義高級表達式,如如下示例所示:

例子57.  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,如圖如下示例:

示例58.使用@Query在查詢方法中聲明本機查詢
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而言,它沒法可靠地進行操做。可是,您能夠經過本身指定計數查詢來使用本機查詢進行分頁,如如下示例所示:
示例59.經過使用,在查詢方法中聲明本機計數查詢以進行分頁  @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。使用排序

排序能夠經過提供PageRequestSort直接使用來完成Order實例中實際使用的屬性Sort須要與您的域模型匹配,這意味着它們須要解析爲查詢中使用的屬性或別名。JPQL將其定義爲狀態字段路徑表達式。

  使用任何不可引用的路徑表達式會致使一個Exception

可是,Sort一塊兒使用@Query可讓您潛入Order包含ORDER BY子句中的函數的未經路徑檢查的實例中這是可能的,由於Order它附加到給定的查詢字符串。默認狀況下,Spring Data JPA拒絕Order包含函數調用的任何實例,但能夠JpaSort.unsafe用來添加潛在的不安全排序。

如下示例使用SortJpaSort,包括一個不安全的選項JpaSort

例子60.使用 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註釋爲方法參數提供具體名稱,並在查詢中綁定名稱,如如下示例中所示:

例子61.使用命名參數
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 entityNameof 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:

示例62.在存儲庫查詢方法中使用SpEL表達式 - entityName
@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.xmlSpEL表達式不支持自定義

固然,您能夠User直接在查詢聲明中使用,但這也須要您更改查詢。該類引用#entityNameUser類的潛在將來從新映射轉換爲不一樣的實體名稱(例如,經過使用@Entity(name = "MyUser")

#{#entityName}查詢字符串中表達式的另外一個用例是,若是要爲具體的域類型定義具備專用存儲庫接口的通用存儲庫接口。爲了避免重複定義具體接口上的​​自定義查詢方法,能夠@Query在通用資源庫接口中的註釋的查詢字符串中使用實體名稱表達式,如如下示例所示:

例63.在存儲庫查詢方法中使用SpEL表達式 - 具備繼承性的entityName
@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,如如下示例所示:

例64.聲明操做查詢
@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查詢,如如下示例所示:

例65.使用派生的刪除查詢
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批註數組和一個布爾標誌來禁用應用於應用分頁時觸發的附加計數查詢的提示,如如下示例所示:

示例66.將QueryHints與存儲庫方法一塊兒使用
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定義。您能夠在實體上使用該註釋來配置生成的查詢的提取計劃。獲取的類型(FetchLoad)可使用註釋中type屬性進行配置@EntityGraph請參閱JPA 2.1規範3.7.4以供進一步參考。

如下示例顯示如何在實體上定義命名實體圖:

例67.在實體上定義一個命名實體圖。
@Entity @NamedEntityGraph(name = "GroupInfo.detail", attributeNodes = @NamedAttributeNode("members")) public class GroupInfo { // default fetch mode is lazy. @ManyToMany List<GroupMember> members = new ArrayList<GroupMember>();  }

如下示例顯示如何引用存儲庫查詢方法上的命名實體圖:

示例68.引用存儲庫查詢方法上的命名實體圖定義。
@Repository public interface GroupRepository extends CrudRepository<GroupInfo, String> { @EntityGraph(value = "GroupInfo.detail", type = EntityGraphType.LOAD) GroupInfo getByGroupName(String name); }

也能夠經過使用來定義臨時實體圖@EntityGraph提供attributePaths的內容EntityGraph不須要顯式添加@NamedEntityGraph到您的域類型就能夠翻譯成相應的內容,如如下示例所示:

示例69.在存儲庫查詢方法上使用AD-HOC實體圖定義。
@Repository public interface GroupRepository extends CrudRepository<GroupInfo, String> { @EntityGraph(attributePaths = { "members" }) GroupInfo getByGroupName(String name); }

3.3.11。預測

Spring Data查詢方法一般會返回存儲庫管理的聚合根的一個或多個實例。可是,有時可能須要根據這些類型的某些屬性建立投影。Spring Data容許對專用返回類型進行建模,以更有選擇地檢索托管集合的部分視圖。

設想一個存儲庫和聚合根類型,例如如下示例:

示例70.樣本聚合和存儲庫
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提供了什麼手段來實現這一目標?本章的其他部分回答了這個問題。

基於接口的投影

將查詢結果僅限於名稱屬性的最簡單方法是聲明一個接口,該接口公開要讀取的屬性的訪問器方法,如如下示例所示:

示例71.用於檢索屬性子集的投影界面
interface NamesOnly { String getFirstname(); String getLastname(); }

這裏重要的一點是,這裏定義的屬性徹底匹配聚合根中的屬性。這樣作能夠添加查詢方法,以下所示:

示例72.使用基於接口的投影和查詢方法的存儲庫
interface PersonRepository extends Repository<Person, UUID> { Collection<NamesOnly> findByLastname(String lastname); }

查詢執行引擎在運行時爲每一個返回的元素建立該接口的代理實例,並將調用轉發給目標對象的公開方法。

能夠遞歸地使用投影。若是您還想包含一些Address信息,請爲其建立一個投影界面,並從聲明中返回該界面getAddress(),如如下示例所示:

示例73.用於檢索屬性子集的投影界面
interface PersonSummary { String getFirstname(); String getLastname(); AddressSummary getAddress(); interface AddressSummary { String getCity(); } }

在方法調用中,address獲取目標實例屬性,並依次將其包裝到投影代理中。

已關閉的投影

其訪問器方法所有匹配目標聚合的屬性的投影接口被認爲是閉合投影。下面的例子(咱們在本章前面也使用過)是一個封閉的投影:

例74.一個封閉的投影
interface NamesOnly { String getFirstname(); String getLastname(); }

若是您使用封閉投影,Spring Data能夠優化查詢執行,由於咱們知道支持投影代理所需的全部屬性。有關更多詳細信息,請參閱參考文檔中特定於模塊的部分。

打開預測

投影接口中的訪問器方法也可用於經過使用@Value註釋來計算新值,如如下示例所示:

例子75.一個公開的投影
interface NamesOnly { @Value("#{target.firstname + ' ' + target.lastname}") String getFullName();  }

支持投影的聚合根在target變量中可用投影界面使用@Value是一個開放的投影。在這種狀況下,Spring Data不能應用查詢執行優化,由於SpEL表達式可使用聚合根的任何屬性。

使用的表達式@Value不該該太複雜 - 你想避免在String變量中編程對於很是簡單的表達式,一個選項可能會採起默認方法(在Java 8中引入),如如下示例所示:

例76.一個投影界面使用默認方法來定製邏輯
interface NamesOnly { String getFirstname(); String getLastname(); default String getFullName() { return getFirstname.concat(" ").concat(getLastname()); } }

這種方法要求您可以純粹基於投影界面上公開的其餘訪問器方法來實現邏輯。第二種更靈活的選擇是在Spring bean中實現自定義邏輯,而後從SpEL表達式中調用它,如如下示例所示:

示例77.示例Person對象
@Component class MyBean { String getFullName(Person person) {  } } interface NamesOnly { @Value("#{@myBean.getFullName(target)}") String getFullName();  }

注意SpEL表達式如何引用myBean並調用getFullName(…)方法並將投影目標做爲方法參數轉發。SpEL表達評估支持的方法也可使用方法參數,而後能夠從表達式中引用方法參數。方法參數可經過Object名爲數組得到args如下示例顯示如何從args數組中獲取方法參數

示例78.示例Person對象
interface NamesOnly { @Value("#{args[0] + ' ' + target.firstname + '!'}") String getSalutation(String prefix); }

一樣,對於更復雜的表達式,您應該使用Spring bean並讓表達式調用一個方法,如前所述

基於類別的預測(DTO)

定義投影的另外一種方法是使用值類型DTO(數據傳輸對象),它們爲應該檢索的字段保存屬性。這些DTO類型的使用方式與投影界面的使用方式徹底相同,只是沒有代理髮生,也不能應用嵌套投影。

若是商店經過限制要加載的字段來優化查詢執行,則要從所公開的構造函數的參數名稱中肯定要加載的字段。

如下示例顯示了投影DTO:

例79.一個投影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註釋(不要與@Value前面的界面示例中顯示的Spring 註釋混淆)。若是您使用Project Lombok的@Value註釋,則前面顯示的示例DTO將變爲如下內容:

@Value class NamesOnly { String firstname, lastname; }

字段private final默認狀況下,該類公開一個構造函數,該構造函數接受全部字段並自動獲取equals(…)hashCode()實現方法。

動態投影

到目前爲止,咱們已經使用投影類型做爲集合的返回類型或元素類型。可是,您可能須要選擇在調用時使用的類型(這會使其變爲動態)。要應用動態投影,請使用查詢方法,以下例所示:

示例80.使用動態投影參數的存儲庫
interface PersonRepository extends Repository<Person, UUID> { <T> Collection<T> findByLastname(String lastname, Class<T> type); }

這樣,可使用該方法按原樣或使用投影來獲取聚合,如如下示例所示:

示例81.使用具備動態投影的存儲庫
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在存儲庫方法中聲明存儲過程元數據註釋。

下面的示例使用如下過程:

例82.  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在實體類型上使用註釋來配置

例83.在實體上存儲過程元數據定義。
@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 {}

您能夠經過多種方式從存儲庫方法中引用存儲過程。要調用的存儲過程既可使用註釋valueor procedureName屬性直接定義,也可使用該屬性@Procedure間接定義name若是沒有配置名稱,則使用存儲庫方法的名稱做爲回退。

如下示例顯示如何引用明確映射的過程:

示例84:在數據庫中引用具備名稱「plus1inout」的顯式映射過程。
@Procedure("plus1inout") Integer explicitlyNamedPlus1inout(Integer arg);

如下示例顯示如何使用procedureName別名引用隱式映射的過程

示例85.經過 procedureName 別名在數據庫中引用名爲「plus1inout」的隱式映射過程
@Procedure(procedureName = "plus1inout") Integer plus1inout(Integer arg);

如下示例顯示如何在如下位置引用顯式映射的命名過程EntityManager

示例86.引用明確映射的命名存儲過程「User.plus1IO」  EntityManager
@Procedure(name = "User.plus1IO") Integer entityAnnotatedCustomNamedProcedurePlus1IO(@Param("arg") Integer arg);

如下示例顯示如何EntityManager使用方法名稱引用隱式命名的存儲過程

示例87.  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而無需爲每一個須要的組合聲明查詢(方法),如如下示例所示:

示例88.客戶的規範
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以下:

例89.使用一個簡單的規範
List<Customer> customers = customerRepository.findAll(isLongTermCustomer());

爲何不爲這種數據訪問建立查詢?使用單個Specification查詢聲明不會得到比普通查詢聲明更多的好處。規範的力量真正發揮出來,當你把它們組合起來創造新的Specification物體。您能夠經過Specifications咱們提供幫助器類來實現此目的,以構建相似於如下內容的表達式:

例90.組合的規格
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)

  • 僅支持字符串的開始/包含/結束/正則表達式匹配以及其餘屬性類型的精確匹配。

在開始使用示例查詢以前,您須要有一個域對象。要開始,請爲您的存儲庫建立一個接口,如如下示例所示:

示例91.示例Person對象
public class Person { @Id private String id; private String firstname; private String lastname; private Address address; // … getters and setters omitted }

上例顯示了一個簡單的域對象。你能夠用它來建立一個Example默認狀況下,具備null值的字段將被忽略,而且字符串將經過使用特定於商店的默認值進行匹配。示例能夠經過使用of工廠方法或使用來構建ExampleMatcherExample是不可變的。如下清單顯示了一個簡單的示例:

例92.簡單的例子
Person person = new Person();  person.setFirstname("Dave");  Example<Person> example = Example.of(person);
  建立一個域對象的新實例。
  設置要查詢的屬性。
  建立Example

理想狀況下,示例將使用存儲庫執行。爲此,請讓您的存儲庫接口擴展QueryByExampleExecutor<T>如下清單顯示了QueryByExampleExecutor界面的摘錄

例93.  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爲字符串匹配,空值處理和特定於屬性的設置指定您本身的缺省值,如如下示例所示:

示例94.具備定製匹配的示例匹配器
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」)。您可使用匹配選項和區分大小寫來調整它,如如下示例所示:

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

另外一種配置匹配器選項的方法是使用lambdas(在Java 8中引入)。這種方法建立了一個回調,要求實現者修改匹配器。您無需返回匹配器,由於配置選項在匹配器實例中保存。如下示例顯示使用lambdas的匹配器:

示例96.使用lambdas配置匹配器選項
ExampleMatcher matcher = ExampleMatcher.matching() .withMatcher("firstname", match -> match.endsWith()) .withMatcher("firstname", match -> match.startsWith()); }

經過Example使用配置的合併視圖建立的查詢默認匹配設置能夠在ExampleMatcher級別上設置,而單獨的設置能夠應用於特定的屬性路徑。設置的設置ExampleMatcher將由屬性路徑設置繼承,除非它們是明肯定義的。屬性修補程序上的設置優先於默認設置。下表介紹了各類ExampleMatcher設置的範圍

表4.  ExampleMatcher 設置的範圍
設置 範圍

空操做

ExampleMatcher

字符串匹配

ExampleMatcher 和財產路徑

忽略屬性

屬性路徑

區分大小寫

ExampleMatcher 和財產路徑

價值轉化

屬性路徑

3.6.4。執行一個例子

在Spring Data JPA中,您能夠將示例查詢與存儲庫一塊兒使用,如如下示例所示:

示例97.使用存儲庫的示例查詢
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屬性能夠用於屬性匹配。

屬性說明符接受屬性名稱(如firstnamelastname)。您能夠經過連接屬性與點(address.city進行導航您還可使用匹配選項和區分大小寫來調整它。

下表顯示了StringMatcher您可使用的各類選項以及在名爲的字段上使用它們的結果firstname

表5.  StringMatcher 選項
匹配 邏輯結果

DEFAULT (區分大小寫)

firstname = ?0

DEFAULT (不區分大小寫)

LOWER(firstname) = LOWER(?0)

EXACT (區分大小寫)

firstname = ?0

EXACT (不區分大小寫)

LOWER(firstname) = LOWER(?0)

STARTING (區分大小寫)

firstname like ?0 + '%'

STARTING (不區分大小寫)

LOWER(firstname) like LOWER(?0) + '%'

ENDING (區分大小寫)

firstname like '%' + ?0

ENDING (不區分大小寫)

LOWER(firstname) like '%' + LOWER(?0)

CONTAINING (區分大小寫)

firstname like '%' + ?0 + '%'

CONTAINING (不區分大小寫)

LOWER(firstname) like '%' + LOWER(?0) + '%'

3.7。事務性

默認狀況下,存儲庫實例上的CRUD方法是事務性的。對於讀操做,事務配置readOnly標誌設置爲true全部其餘人都配置爲純文本,@Transactional以便應用默認事務配置。有關詳細信息,請參閱JavaDoc SimpleJpaRepository若是您須要調整存儲庫中聲明的某個方法的事務配置,請在存儲庫接口中從新聲明該方法,以下所示:

示例98. CRUD的自定義事務配置
public interface UserRepository extends CrudRepository<User, Long> { @Override @Transactional(timeout = 10) public List<User> findAll(); // Further query method declarations }

這樣作會致使findAll()方法以10秒的超時運行而且沒有readOnly標誌。

另外一種改變事務行爲的方式是使用一個(一般)覆蓋多個存儲庫的外觀或服務實現。其目的是爲非CRUD操做定義事務邊界。如下示例顯示如何爲多個存儲庫使用這種外觀:

示例99.使用Facade爲多個存儲庫調用定義事務
@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在您定義的存儲庫接口上使用,如如下示例所示:

示例100.在查詢方法中使用@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

 

您能夠將事務用於只讀查詢,並經過設置readOnly標誌來標記它們可是,這樣作並不能檢查您是否觸發操做查詢(儘管某些數據庫在只讀事務中拒絕INSERTUPDATE聲明)。readOnly相反,將該標誌做爲提示傳播到基礎JDBC驅動程序以進行性能優化。此外,Spring對底層的JPA提供者進行一些優化。例如,當與Hibernate一塊兒使用時,刷新模式被設置爲NEVER當您將事務配置爲時readOnly,這將致使Hibernate跳過髒檢查(對大型對象樹顯着改進)。

3.8。鎖定

要指定要使用的鎖定模式,能夠@Lock在查詢方法上使用註釋,如如下示例所示:

例101.定義查詢方法的鎖定元數據
interface UserRepository extends Repository<User, Long> { // Plain query method @Lock(LockModeType.READ) List<User> findByLastname(String lastname); }

這個方法聲明將致使觸發查詢中配備了LockModeTypeREAD您還能夠經過在存儲庫接口中從新聲明它們並添加@Lock註釋來爲CRUD方法定義鎖定,如如下示例所示:

示例102.在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捕捉時的變化發生了。

例103.被審計單位
class Customer { @CreatedBy private User user; @CreatedDate private DateTime createdDate; // … further properties omitted }

正如您所看到的,註釋能夠選擇性地應用,具體取決於您要捕獲的信息。更改時間捕捉註釋能夠在類型喬達,時間,性質使用DateTime,遺留Java DateCalendar,JDK8日期和時間類型,以及longLong

基於接口的審計元數據

若是您不想使用註釋來定義審計元數據,則可讓您的域類實現該Auditable界面。它公開了全部審計屬性的setter方法。

還有一個便利的基類,AbstractAuditable您能夠擴展該基類以免須要手動實現接口方法。這樣作會增長域類與Spring Data的耦合,這多是您想要避免的。一般,定義審計元數據的基於註釋的方式是首選,由於它侵入性更小,更靈活。

AuditorAware

若是您使用@CreatedBy或者@LastModifiedBy,審計基礎設施須要知道當前的委託人。爲此,咱們提供一個AuditorAware<T>SPI接口,您必須實施接口,以告知基礎架構當前用戶或系統與應用程序進行交互的人員。泛型T定義了哪些類型的屬性註釋@CreatedBy@LastModifiedBy必須是。

如下示例顯示了使用Spring Security Authentication對象的接口的實現

例104.基於Spring Security的AuditorAware的實現
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(); } }

該實現訪問AuthenticationSpring Security提供對象,並查找UserDetails您在UserDetailsService實現中建立的自定義實例咱們在這裏假設你正在經過UserDetails實現暴露域用戶,可是根據Authentication找到的,你也能夠從任何地方查看它。:leveloffset:-1

3.9.2。JPA審計

通常審計配置

Spring Data JPA附帶一個實體監聽器,可用於觸發捕獲審計信息。首先,您必須AuditingEntityListenerorm.xml文件內的持久性上下文中註冊要用於全部實體的對象,如如下示例所示:

示例105.審計配置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命名空間元素添加到您的配置中,以下所示:

示例106.使用XML配置激活審計
<jpa:auditing auditor-aware-ref="yourAuditorAwareBean" />

從Spring Data JPA 1.5開始,您能夠經過使用註釋註釋配置類來啓用審計@EnableJpaAuditing您仍然必須修改該orm.xml文件並spring-aspects.jar在類路徑中進行。如下示例顯示如何使用@EnableJpaAuditing註釋:

示例107.使用Java配置激活審計
@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在自定義存儲庫中使用:

示例108.  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基於名稱自動合併持久性單元實現,以下例所示:

例109.使用MergingPersistenceUnitmanager
<bean class="….LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitManager"> <bean class="….MergingPersistenceUnitManager" /> </property> </bean>
類路徑掃描@Entity類和JPA映射文件

簡單的JPA設置須要列出全部註解映射的實體類orm.xml這一樣適用於XML映射文件。Spring Data JPA提供了一個ClasspathScanningPersistenceUnitPostProcessor能夠獲取配置的基礎包並可選擇使用映射文件名模式。而後,它會掃描給定的包中用@Entityor 註釋的類@MappedSuperclass,並加載與文件名模式匹配的配置文件,並將它們傳遞給JPA配置。後處理器必須配置以下:

例子110.使用ClasspathScanningPersistenceUnitPostProcessor
<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。

您如今能夠經過爲EntityManagerFactoryand 執行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 />元素的屬性

表6.屬性
名稱 描述

base-package

定義要*Repository在自動檢測模式下擴展的存儲庫接口的掃描包(實際接口由特定的Spring Data模塊肯定)。掃描配置軟件包下面的全部軟件包。通配符是容許的。

repository-impl-postfix

定義後綴以自動檢測自定義存儲庫實現。名稱以配置的後綴結尾的類被視爲候選。默認爲Impl

query-lookup-strategy

肯定用於建立查找器查詢的策略。有關詳細信息,請參閱「 查詢查找策略 」。默認爲create-if-not-found

named-queries-location

定義搜索包含外部定義的查詢的屬性文件的位置。

consider-nested-repositories

是否應該考慮嵌套的存儲庫接口定義。默認爲false

附錄B:Poppers命名空間參考

<populator />元素

<populator />元素容許經過Spring Data存儲庫基礎結構填充數據存儲。1 ]

表7.屬性
名稱 描述

locations

在哪裏能夠找到要從存儲庫中讀取對象的文件。

附錄C:存儲庫查詢關鍵字

支持的查詢關鍵字

下表列出了Spring Data存儲庫查詢派生機制一般支持的關鍵字。可是,請參閱特定商店的文檔以獲取支持的關鍵字的確切列表,由於此處列出的某些關鍵字可能在特定商店中不受支持。

表8.查詢關鍵字
邏輯關鍵字 關鍵字表達

AND

And

OR

Or

AFTER

After, IsAfter

BEFORE

Before, IsBefore

CONTAINING

ContainingIsContainingContains

BETWEEN

Between, IsBetween

ENDING_WITH

EndingWithIsEndingWithEndsWith

EXISTS

Exists

FALSE

False, IsFalse

GREATER_THAN

GreaterThan, IsGreaterThan

GREATER_THAN_EQUALS

GreaterThanEqual, IsGreaterThanEqual

IN

In, IsIn

IS

Is,,Equals(或沒有關鍵字)

IS_EMPTY

IsEmpty, Empty

IS_NOT_EMPTY

IsNotEmpty, NotEmpty

IS_NOT_NULL

NotNull, IsNotNull

IS_NULL

Null, IsNull

LESS_THAN

LessThan, IsLessThan

LESS_THAN_EQUAL

LessThanEqual, IsLessThanEqual

LIKE

Like, IsLike

NEAR

Near, IsNear

NOT

Not, IsNot

NOT_IN

NotIn, IsNotIn

NOT_LIKE

NotLike, IsNotLike

REGEX

RegexMatchesRegexMatches

STARTING_WITH

StartingWithIsStartingWithStartsWith

TRUE

True, IsTrue

WITHIN

Within, IsWithin

附錄D:存儲庫查詢返回類型

支持的查詢返回類型

下表列出了Spring Data存儲庫一般支持的返回類型。可是,請參閱特定於商店的文檔以獲取受支持的返回類型的確切列表,由於此處列出的某些類型可能在特定商店中不受支持。

  地理空間類型(如GeoResultGeoResults,和GeoPage)是僅對支持地理空間查詢數據存儲可用。
表9.查詢返回類型
返回類型 描述

void

表示沒有返回值。

基元

Java基元。

包裝類型

Java包裝類型。

T

一個獨特的實體。指望查詢方法最多返回一個結果。若是沒有找到結果,null則返回。多個結果觸發一個IncorrectResultSizeDataAccessException

Iterator<T>

一個Iterator

Collection<T>

Collection

List<T>

List

Optional<T>

Java 8或番石榴Optional指望查詢方法最多返回一個結果。若是沒有找到結果,Optional.empty()或者Optional.absent()返回。多個結果觸發一個IncorrectResultSizeDataAccessException

Option<T>

Scala或Javalang Option類型。語義上與Optional前面描述的Java 8相同

Stream<T>

Java 8 Stream

Future<T>

Future指望一個方法被註釋@Async而且要求啓用Spring的異步方法執行能力。

CompletableFuture<T>

Java 8 CompletableFuture指望一個方法被註釋@Async而且要求啓用Spring的異步方法執行能力。

ListenableFuture

org.springframework.util.concurrent.ListenableFuture指望一個方法被註釋@Async而且要求啓用Spring的異步方法執行能力。

Slice

大小的數據塊,指示是否有更多數據可用。須要一個Pageable方法參數。

Page<T>

Slice附加信息,如結果總數。須要一個Pageable方法參數。

GeoResult<T>

帶有附加信息的結果輸入,例如到參考位置的距離。

GeoResults<T>

GeoResult<T>附加信息列表,例如參考位置的平均距離。

GeoPage<T>

PageGeoResult<T>,如到參考位置的平均距離。

Mono<T>

Mono使用反應性儲存庫發射零個或一個元素的項目反應器指望查詢方法最多返回一個結果。若是沒有找到結果,Mono.empty()則返回。多個結果觸發一個IncorrectResultSizeDataAccessException

Flux<T>

項目反應堆Flux使用反應性儲存庫發射零個,一個或多個元素。返回的查詢Flux能夠發出無數個元素。

Single<T>

RxJava Single使用反應性倉庫發射單個元素。指望查詢方法最多返回一個結果。若是沒有找到結果,Mono.empty()則返回。多個結果觸發一個IncorrectResultSizeDataAccessException

Maybe<T>

一個RxJava Maybe使用被動存儲庫發射零個或一個元素。指望查詢方法最多返回一個結果。若是沒有找到結果,Mono.empty()則返回。多個結果觸發一個IncorrectResultSizeDataAccessException

Flowable<T>

一個RxJava Flowable使用被動存儲庫發射零個,一個或多個元素。返回的查詢Flowable能夠發出無數個元素。

附錄E:常見問題

共同

  1. 我想得到關於哪些方法在內部調用的更詳細的日誌記錄信息JpaRepository(例如)。我如何得到它們?

    您可使用CustomizableTraceInterceptorSpring提供的,如如下示例所示:

    <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>

基礎設施

  1. 目前我已經實現了基於的存儲庫層HibernateDaoSupportSessionFactory使用Spring 建立了一個AnnotationSessionFactoryBean我如何讓Spring Data存儲庫在這個環境中工做?

    必須更換AnnotationSessionFactoryBeanHibernateJpaSessionFactoryBean以下:

    例111.  SessionFactory 從a中查找a HibernateEntityManagerFactory
    <bean id="sessionFactory" class="org.springframework.orm.jpa.vendor.HibernateJpaSessionFactoryBean"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean>

審計

  1. 我想使用Spring Data JPA審計功能,可是個人數據庫已經配置爲在實體上設置修改和建立日期。如何防止Spring Data以編程方式設置日期。

    名稱空間元素set-dates屬性設置auditingfalse

附錄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


1 請參閱 XML配置
相關文章
相關標籤/搜索