Spring Data JPA文檔筆記

3.1. 核心概念

CrudRepository包含增刪查改基礎(chǔ)功能

public interface CrudRepository<T, ID extends Serializable>
  extends Repository<T, ID> {

  <S extends T> S save(S entity);      //保存給定實(shí)體

  Optional<T> findById(ID primaryKey); //根據(jù)給定主鍵id查找實(shí)體

  Iterable<T> findAll();               //返回所有實(shí)體

  long count();                        //返回實(shí)體數(shù)量

  void delete(T entity);               //刪除給定實(shí)體

  boolean existsById(ID primaryKey);   //根據(jù)給定實(shí)體判斷一個(gè)實(shí)體是否存在

  // … more functionality omitted.
}

PagingAndSortingRepository 繼承自 CrudRepository并提供分頁和排序功能

public interface PagingAndSortingRepository<T, ID extends Serializable>
  extends CrudRepository<T, ID> {

  Iterable<T> findAll(Sort sort);

  Page<T> findAll(Pageable pageable);
}

如果要在以20為一頁的結(jié)果中,獲取第2頁結(jié)果,則如下使用:

PagingAndSortingRepository<User, Long> repository = // … get access to a bean
Page<User> users = repository.findAll(new PageRequest(1, 20));

除了根據(jù)方法查詢僚祷,還可以查詢數(shù)量和刪除缎脾。

interface UserRepository extends CrudRepository<User, Long> {
//根據(jù) lastname來得到數(shù)量贤重,相當(dāng)于 select count(*) from table  t where t.lastname= lastname
  long countByLastname(String lastname);
}
interface UserRepository extends CrudRepository<User, Long> {
//根據(jù)lastname來刪除赏殃,返回刪除的數(shù)量,類似于 delete from table t where t.lastname = lastname
  long deleteByLastname(String lastname);
//根據(jù)lastname來刪除皇耗,返回被刪除的實(shí)體集合
  List<User> removeByLastname(String lastname);
}

3.2 方法查詢

步驟:

  1. 聲明一個(gè)接口萌丈,繼承 Repository 或它的一個(gè)子類赞哗,你要加上對(duì)應(yīng)的實(shí)體類和對(duì)應(yīng)的主鍵、
interface PersonRepository extends Repository<Person, Long> { … }
  1. 在接口中定義方法查詢
interface PersonRepository extends Repository<Person, Long> {
  List<Person> findByLastname(String lastname);
}
  1. 創(chuàng)建接口的代理實(shí)例來讓Spring管理辆雾,可以通過 Java方式來配置
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@EnableJpaRepositories
class Config {}

或通過 xml 來配置

<?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的命名空間需要你自己作對(duì)應(yīng)的修改肪笋。

  1. 注入 repository 實(shí)例并使用
class SomeClient {

  private final PersonRepository repository;

  SomeClient(PersonRepository repository) {
    this.repository = repository;
  }

  void doSomething() {
    List<Person> persons = repository.findByLastname("Matthews");
  }
}

下面的部分將詳細(xì)說明每一步。

3.3 定義repository接口

定義實(shí)體類的repository接口度迂,必須繼承Repository并加上相應(yīng)的實(shí)體類和ID類型藤乙,如果你實(shí)體想獲得 CRUD 方法,那就繼承 CrudRepository 惭墓,而不是 Repository

3.3.1

通常坛梁,你的 repository接口會(huì)繼承 RepositoryCrudRepositoryPagingAndSortingRepository腊凶。當(dāng)然了划咐,如果你不想繼承這些Spring Data 內(nèi)置的接口毅人,你可以通過在repository上使用 @RepositoryDefinition注解。繼承 CrudRepository 可以暴露一些操作實(shí)體的方法尖殃,你可以從 CrudRepository 中將你想要的功能復(fù)制進(jìn)你的 repository中。

這將使你能定義自己的頂層 Spring Data Repository划煮。

選擇性地暴露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);
}

包配置自動(dòng)

@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
interface Configuration { }

3.4 查詢方法

repository可以通過方法名(這個(gè)好哇~)或手動(dòng)查詢(@Query注解)來執(zhí)行查詢

3.4.1

interface PersonRepository extends Repository<User, Long> {

  List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);

  // 啟用去重查詢
  List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
  List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);

  // 某個(gè)屬性忽略大小寫
  List<Person> findByLastnameIgnoreCase(String lastname);
  // 所有屬性忽略大小寫
  List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);

  // 查詢中使用 Order By
  List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
  List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}
  • 在表達(dá)式中還可以使用 AND OR Between LessThan GreaterThan Like 等送丰,請(qǐng)參閱手冊(cè)
  • IgnoreCase 單個(gè)屬性忽略大小寫,AllIgnoreCase 所有屬性忽略大小寫
  • OrderBy 排序 Asc 或 Desc(降序)

3.4.3 屬性表達(dá)式

如果 Person 中的 Address屬性有 ZipCode屬性弛秋,則可以如下:

List<Person> findByAddressZipCode(ZipCode zipCode);

會(huì)解析為 x.address.zipCode器躏,算法從右邊開始檢測(cè),會(huì)先將它分為 AddressZipCode 蟹略,但是匹配失敗登失,于是分為 AddressZipCode

在第一次匹配成功的話挖炬,便會(huì)忽略下一個(gè)可能的匹配揽浙,假如 Person 還有一個(gè)屬性 為 addressZip, 而算法檢測(cè)出 addressZip 無 Code 屬性意敛,于是報(bào)錯(cuò)馅巷。

為了避免混淆,可以使用 _ 來分開它們草姻。

List<Person> findByAddress_ZipCode(ZipCode zipCode);

雖然這也能解析正確钓猬,但是我們最好還是按Java命名規(guī)范來命名屬性(別使用_,而使用駝峰記法)

3.4.4 特殊的處理參數(shù)

Example 14 在查詢方法中使用 Pageable,Slice和Sort

Page<User> findByLastname(String lastname, Pageable pageable);

Slice<User> findByLastname(String lastname, Pageable pageable);

List<User> findByLastname(String lastname, Sort sort);

List<User> findByLastname(String lastname, Pageable pageable);

Page包今所有元素的數(shù)量和頁撩独,它通過觸發(fā)查詢數(shù)量來實(shí)現(xiàn)敞曹,所以這有可能會(huì)花費(fèi)很長時(shí)間,取決于存儲(chǔ)的數(shù)據(jù)量综膀。而Slice就不一樣了澳迫,它只知道是否有下一個(gè)Slice可用,在大量數(shù)據(jù)集中這很實(shí)用僧须。

3.4.5 限制查詢結(jié)果

查詢方法的結(jié)果可以通過關(guān)鍵字first或top進(jìn)行限制纲刀,可以互換使用。 可以在
first/top 后附加到指定要返回的最大結(jié)果大小担平。 如果該數(shù)字被遺漏示绊,則假設(shè)結(jié)果大小為1。

Example 15暂论,Top和First來限制結(jié)果集大小

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

限制表達(dá)式還支持 Distinct 關(guān)鍵字面褐。 此外,對(duì)于將結(jié)果集限制為一個(gè)實(shí)例的查詢取胎,支持將結(jié)果包裝為可選 Optional展哭。

如果分頁或切片(Slice)應(yīng)用于限制查詢分頁(以及可用頁數(shù)的計(jì)算)湃窍,則在有限的結(jié)果內(nèi)應(yīng)用。

請(qǐng)注意匪傍,通過Sort參數(shù)將結(jié)果與動(dòng)態(tài)排序結(jié)合使用您市,可以表達(dá)'K'最小以及'K'最大元素的查詢方法。

3.4.6 流式查詢結(jié)果

可以通過使用Java 8 Stream <T>作為返回類型來逐步處理查詢方法的結(jié)果役衡。 不是將查詢結(jié)果簡單地包含在Stream數(shù)據(jù)存儲(chǔ)中茵休,而是使用特定的方法來執(zhí)行流。

示例16.使用Java 8 Stream <T>流式傳輸查詢的結(jié)果

@Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();

Stream<User> readAllByFirstnameNotNull();

@Query("select u from User u")
Stream<User> streamAllPaged(Pageable pageable);

流可能包裝底層數(shù)據(jù)存儲(chǔ)特定資源手蝎,因此必須在使用后關(guān)閉榕莺。 您可以使用close()方法或使用Java 7 try-with-resources塊手動(dòng)關(guān)閉Stream。

示例17. 在一個(gè)try-with-resources塊中使用Stream <T>

try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) {
  stream.forEach(…);
}

并不是所有的Spring數(shù)據(jù)模塊目前都支持Stream <T>作為返回類型棵介。

3.4.7 異步查詢結(jié)果####

可以使用Spring的異步方法執(zhí)行功能異步執(zhí)行存儲(chǔ)庫(respository)查詢钉鸯。 這意味著方法將在調(diào)用時(shí)立即返回,并且實(shí)際的查詢執(zhí)行將發(fā)生在已提交給Spring TaskExecutor的任務(wù)中邮辽。

//java.util.concurrent.Future作為返回類型
@Async
Future<User> findByFirstname(String firstname);               
//返回類型為Java 8的 java.util.concurrent.CompletableFuture
@Async
CompletableFuture<User> findOneByFirstname(String firstname); 
//返回類型為org.springframework.util.concurrent.ListenableFuture
@Async
ListenableFuture<User> findOneByLastname(String lastname);    

3.5 創(chuàng)建存儲(chǔ)庫(respository)實(shí)例

在本節(jié)中唠雕,您將為定義的存儲(chǔ)庫接口創(chuàng)建實(shí)例和bean定義。 一種方法是使用支持存儲(chǔ)庫機(jī)制的每個(gè)Spring數(shù)據(jù)模塊附帶的Spring命名空間吨述,盡管我們通常建議使用Java-Config風(fēng)格配置及塘。

3.5.1 XML 配置

每個(gè)Spring Data模塊都包含一個(gè)存儲(chǔ)庫元素,它允許您簡單地定義Spring為您掃描的基礎(chǔ)包锐极。

示例18.通過XML啟用Spring數(shù)據(jù)存儲(chǔ)庫

<?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及其所有子包,用于擴(kuò)展Repository或其一個(gè)子接口的接口灵再。 對(duì)于發(fā)現(xiàn)的每個(gè)接口肋层,基礎(chǔ)架構(gòu)將注冊(cè)持久性技術(shù)特定的FactoryBean,以創(chuàng)建處理查詢方法調(diào)用的相應(yīng)代理翎迁。 每個(gè)bean都是根據(jù)從接口名稱派生的bean名稱注冊(cè)的栋猖,所以UserRepository的接口將被注冊(cè)在userRepository下。 base-package屬性允許通配符汪榔,以便您可以定義掃描包的模式蒲拉。

使用過濾器

默認(rèn)情況下,框架會(huì)自動(dòng)檢查每個(gè)接口痴腌,并擴(kuò)展位于配置的基礎(chǔ)包下方的持久化特定的Repository子接口雌团,并為其創(chuàng)建一個(gè)bean實(shí)例。 但是士聪,您可能需要對(duì)要為其創(chuàng)建的接口bean實(shí)例進(jìn)行更細(xì)粒度的控制锦援。 為此,您可以在<repositories />中使用<include-filter />和<exclude-filter />元素剥悟。 語義與Spring的上下文命名空間中的元素完全相同灵寺。 有關(guān)詳細(xì)信息曼库,請(qǐng)參閱 Spring參考文檔

例如略板,要將某些接口從存儲(chǔ)庫實(shí)例化中排除毁枯,可以使用以下配置:

示例19.使用exclude-filter元素

<repositories base-package="com.acme.repositories">
  <context:exclude-filter type="regex" expression=".*SomeRepository" />
</repositories>

此示例將以實(shí)例化方式排除以SomeRepository結(jié)尾的所有接口。

3.5.2 Java配置

也可以在Java配置類上使用特定于存儲(chǔ)的@ Enable $ {store}Repositories 注解觸發(fā) repository 架構(gòu)叮称。 有關(guān)Spring容器的基于Java的配置的介紹后众,請(qǐng)參閱參考文檔。1

啟用Spring Data repository 的示例配置看起來像這樣颅拦。

示例20.基于注解的存儲(chǔ)庫配置例子

@Configuration
@EnableJpaRepositories("com.acme.repositories")
class ApplicationConfiguration {

  @Bean
  EntityManagerFactory entityManagerFactory() {
    // …
  }
}

該示例使用JPA特定注釋,您可以根據(jù)實(shí)際使用的 repository 模塊進(jìn)行更改教藻。 這同樣適用于EntityManagerFactory bean 的定義距帅。 請(qǐng)參閱涵蓋 存儲(chǔ) 配置的部分。

3.5.3 單獨(dú)使用

您還可以使用Spring容器外的存儲(chǔ)庫基礎(chǔ)結(jié)構(gòu)括堤,例如 在CDI環(huán)境中碌秸。 您在類路徑中仍然需要一些Spring庫,但通城那裕可以通過編程方式設(shè)置存儲(chǔ)庫讥电。 提供存儲(chǔ)庫支持的Spring數(shù)據(jù)模塊提供了一個(gè)持久化特定的RepositoryFactory,可以如下所示來使用RepositoryFactory轧抗。

示例21.存儲(chǔ)庫工廠的獨(dú)立使用

RepositoryFactorySupport factory = … // 實(shí)例化 factory
UserRepository repository = factory.getRepository(UserRepository.class);

3.6 Spring Data Repository 的自定義實(shí)現(xiàn)

在本節(jié)中恩敌,您將了解自定義Repository以及如何形成復(fù)合存儲(chǔ)庫。

當(dāng)查詢方法需要不同的行為或不能通過查詢推導(dǎo)實(shí)現(xiàn)横媚,很有必要地提供自定義實(shí)現(xiàn)纠炮。 Spring Data Repository 可以輕松地允許您提供自定義 Repository 代碼,并將其與通用CRUD抽象和查詢方法功能集成灯蝴。

3.6.1 定制 Repository

為了豐富具有自定義功能的 Repository恢口,您首先要為自定義功能定義一個(gè)片段接口和一個(gè)實(shí)現(xiàn)。 然后讓您的 Repository 接口從該片段接口擴(kuò)展穷躁。

示例22.自定義Repository接口方法

interface CustomizedUserRepository {
  void someCustomMethod(User user);
}

示例23.實(shí)現(xiàn)自定義的 Repository 接口方法

class CustomizedUserRepositoryImpl implements CustomizedUserRepository {

  public void someCustomMethod(User user) {
    // Your custom implementation
  }
}

該類的最重要的點(diǎn)是與片段接口相比的名稱多了Impl后綴耕肩。

示例24.更改您的 Repository 接口

interface UserRepository extends CrudRepository<User, Long>, CustomizedUserRepository {

  // Declare query methods here
}

讓您的 Repository 接口繼承多一個(gè)接口。 這樣做結(jié)合了CRUD和自定義功能问潭,并將其提供給客戶端猿诸。

Spring Data 提供了很多 Repository ,都可以互相組合來增強(qiáng)你的接口狡忙。 基礎(chǔ) Repository 提代了很多的方法两芳,再加上你自定義的方法,那會(huì)使你的自定義接口更強(qiáng)大去枷。 每次向Repository接口添加新接口時(shí)怖辆,都可以增強(qiáng)你的組合是复。 基礎(chǔ) Repository 接口和相關(guān)實(shí)現(xiàn)由相應(yīng)的 Spring Data 模塊提供。(也就是通過繼承Spring Data提供的一些Repository接口竖螃,可以組成強(qiáng)大的Repository接口淑廊,而且Spring Data提代的Repository接口不用你再去實(shí)現(xiàn),并且很多接口里面已有很多好用的方法 )

示例25.實(shí)現(xiàn)類片段

interface HumanRepository {
  void someHumanMethod(User user);
}

class HumanRepositoryImpl implements HumanRepository {

  public void someHumanMethod(User user) {
    // Your custom implementation
  }
}

interface EmployeeRepository {

  void someEmployeeMethod(User user);

  User anotherEmployeeMethod(User user);
}

class ContactRepositoryImpl implements ContactRepository {

  public void someContactMethod(User user) {
    // Your custom implementation
  }

  public User anotherContactMethod(User user) {
    // Your custom implementation
  }
}

示例26.更改你的Repository 接口

interface UserRepository extends CrudRepository<User, Long>, HumanRepository, ContactRepository {

  // Declare query methods here
}

存儲(chǔ)庫可以由根據(jù)其聲明的順序?qū)氲亩鄠€(gè)自定義實(shí)現(xiàn)構(gòu)成特咆。 自定義實(shí)現(xiàn)具有比基本實(shí)現(xiàn)和存儲(chǔ)庫方面更高的優(yōu)先級(jí)季惩。 這個(gè)順序允許您覆蓋基本存儲(chǔ)庫和方面方法,并且如果兩個(gè)片段貢獻(xiàn)相同的方法簽名腻格,則可以解析歧義画拾。 存儲(chǔ)庫片段不限于在單個(gè)存儲(chǔ)庫界面中使用。 多個(gè)存儲(chǔ)庫可以使用片段接口在不同的存儲(chǔ)庫之間重用定制菜职。

示例27 覆蓋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
  }
}

示例28.定制的存儲(chǔ)庫接口

interface UserRepository extends CrudRepository<User, Long>, CustomizedSave<User> {
}

interface PersonRepository extends CrudRepository<Person, Long>, CustomizedSave<Person> {
}
  • 配置
    如果使用命名空間配置青抛,repository 基礎(chǔ)框架會(huì)嘗試通過掃描我們找到存儲(chǔ)庫的包下面的類來自動(dòng)檢測(cè)自定義實(shí)現(xiàn)片段。這些類需要遵循將命名空間元素的屬性repository-impl-postfix附加到找到的命名約定 片段接口名稱酬核。 此后綴默認(rèn)為Impl蜜另。

示例29.配置示例

<repositories base-package="com.acme.repository" />

<repositories base-package="com.acme.repository" repository-impl-postfix="FooBar" />

第一個(gè)配置示例將嘗試查找一個(gè)類com.acme.repository.CustomizedUserRepositoryImpl作為自定義存儲(chǔ)庫實(shí)現(xiàn),而第二個(gè)示例將嘗試查找com.acme.repository.CustomizedUserRepositoryFooBar嫡意。

  • 解決歧義

如果在不同的包中找到具有匹配類名的多個(gè)實(shí)現(xiàn)举瑰,Spring Data將使用bean名稱來標(biāo)識(shí)哪個(gè)才是正確的。

給定上面介紹的CustomizedUserRepository的以下兩個(gè)自定義實(shí)現(xiàn)蔬螟,第一個(gè)實(shí)現(xiàn)將被選中此迅。 它的bean名稱是customUserRepositoryImpl匹配片段接口(CustomizedUserRepository)加上后綴Impl。

示例30.解決實(shí)現(xiàn)類歧義

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
}

如果您在 UserRepository 接口上使用 @Component(“specialCustom”) 注解旧巾,則 bean 名稱加上Impl匹配為 com.acme.impl.two 中的存儲(chǔ)庫實(shí)現(xiàn)定義邮屁,它將被選擇而不是第一個(gè)。(有注解就按注解的名來匹配~)

  • 手動(dòng)關(guān)聯(lián)

如果您的自定義實(shí)現(xiàn)僅使用基于注釋的配置和自動(dòng)布線菠齿,那么剛才顯示的方法效果很好佑吝,因?yàn)樗鼘⒈灰暈槿魏纹渌鸖pring bean。 如果您的實(shí)現(xiàn)片段bean需要特殊布線绳匀,則只需聲明bean并按照剛才描述的約定命名該bean芋忿。 然后,底層將通過名稱引用手動(dòng)定義的bean疾棵,而不會(huì)自動(dòng)去創(chuàng)建它戈钢。

實(shí)例31.自定義實(shí)現(xiàn)的手動(dòng)關(guān)聯(lián)

<repositories base-package="com.acme.repository" />

<beans:bean id="userRepositoryImpl" class="…">
  <!-- further configuration -->
</beans:bean>

3.6.2 自定義 Base Repository

當(dāng)您要自定義 Base Repository 的方法時(shí),需要自定義所有存儲(chǔ)庫接口是尔,因此所有存儲(chǔ)庫都會(huì)受到影響殉了。 要更改所有存儲(chǔ)庫的行為,您需要?jiǎng)?chuàng)建一個(gè)基于特定實(shí)體類的存儲(chǔ)庫基類的實(shí)現(xiàn)拟枚。 然后薪铜,此類將用作存儲(chǔ)庫代理的自定義基類众弓。

示例32. 自定義 repository 基類

class MyRepositoryImpl<T, ID extends Serializable>
  extends SimpleJpaRepository<T, ID> {

  private final EntityManager entityManager;

  MyRepositoryImpl(JpaEntityInformation entityInformation,
                          EntityManager entityManager) {
    super(entityInformation, entityManager);
    //使 EntityManager 可以在新引入的方法中使用。
    this.entityManager = entityManager;
  }

  @Transactional
  public <S extends T> S save(S entity) {
    // implementation goes here
  }
}

該類需要具有專門的存儲(chǔ)庫工廠實(shí)現(xiàn)使用的超類的構(gòu)造函數(shù)隔箍。 一般來說存儲(chǔ)庫基類具有多個(gè)構(gòu)造函數(shù)谓娃,只要覆蓋其中使用了EntityInformation和底層對(duì)象(例如,EntityManager或模板類)的構(gòu)造函數(shù)即可蜒滩。

最后一步是使Spring Data 架構(gòu)識(shí)別到你定制的基類滨达。 在JavaConfig中,這是通過使用 @ Enable ... Repositories 注解的 repositoryBaseClass 屬性實(shí)現(xiàn)的:

示例33.使用JavaConfig配置自定義存儲(chǔ)庫基類

@Configuration
@EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class)
class ApplicationConfiguration { … }

相應(yīng)的屬性在XML命名空間中可用俯艰。

示例34.使用XML配置自定義存儲(chǔ)庫基類

<repositories base-package="com.acme.repository"
     base-class="….MyRepositoryImpl" />

3.7 從 aggregate roots 建立事件

存儲(chǔ)庫管理的實(shí)體是聚合根(aggregate roots)捡遍。 在 Domain 驅(qū)動(dòng)設(shè)計(jì)應(yīng)用程序中,這些聚合根通常會(huì)發(fā)布域事件竹握。 Spring Data 提供了一個(gè)注釋 @DomainEvents画株,您可以在聚合根的方法上加上它來使該發(fā)布盡可能簡單。

示例35.從聚合根中暴露域事件

class AnAggregateRoot {

    @DomainEvents   ?
    Collection<Object> domainEvents() {
        // … return events you want to get published here
    }

    @AfterDomainEventPublication  ?
    void callbackMethod() {
       // … potentially clean up domain events list
    }
}

? 使用@DomainEvents的方法可以返回單個(gè)事件實(shí)例或事件集合涩搓。 它不能采取任何論據(jù)。
?在所有事件發(fā)布之后劈猪,使用 @AfterDomainEventPublication 注釋的方法昧甘。 可用于清理要發(fā)布的事件列表。

每當(dāng)調(diào)用一個(gè) Spring data repository 的save(...)方法時(shí)战得,都會(huì)調(diào)用這些方法充边。

3.8 空安全

存儲(chǔ)庫方法可以提高空安全性,以便在編譯時(shí)處理空值常侦,而不是在運(yùn)行時(shí)碰到 NullPointerException浇冰。 這使您的應(yīng)用程序通過清理可空性聲明更安全,表達(dá)“值或無價(jià)值”語義聋亡,而無需使用如 Optional 之類的包裝器肘习。

您可以使用Spring Framework的注釋來表達(dá)空安全的存儲(chǔ)庫方法。 它們提供了一個(gè)工具友好的方法坡倔,并在運(yùn)行期間選擇進(jìn)行空檢查:

  • @NonNullApi 注解漂佩,在包級(jí)別將非null作為默認(rèn)行為聲明
  • @Nullable 注解,其中特定參數(shù)或返回值可以為null罪塔。

兩個(gè)注釋都使用JSR-305元注釋進(jìn)行元注釋(一種休眠的JSR投蝉,但由像IDEA,Eclipse征堪,F(xiàn)indbugs等工具所支持)可以為Java開發(fā)人員提供有用的警告瘩缆。

如果您打算使用自己的元注釋,請(qǐng)確保在類路徑中包含包含JSR-305的@Nonnull注釋的JAR文件佃蚜。

在程序包級(jí)或Kotlin中聲明的空聲明范圍內(nèi)的存儲(chǔ)庫查詢方法的調(diào)用在運(yùn)行時(shí)都會(huì)被驗(yàn)證庸娱。 將null值傳遞給不可為空的查詢方法參數(shù)被異常拒絕着绊。 不產(chǎn)生結(jié)果且不可為空的查詢方法將拋出 EmptyResultDataAccessException 而不是返回null。

示例36.激活包的非空默認(rèn)值

@org.springframework.lang.NonNullApi
package com.example;

示例37.聲明參數(shù)和返回值的可空性

package com.example;                                                   ?

interface UserRepository extends Repository<User, String> {

  List<User> findByLastname(@Nullable String firstname); ?              

  @Nullable
  User findByFirstnameAndLastname(String firstname, String lastname); ? 
}

? @NonNullApi在包級(jí)別上聲明此包中的所有API默認(rèn)為非空涌韩。
? @Nullable允許在特定參數(shù)上使用空值畔柔。 每個(gè)可空參數(shù)都必須注釋。
? 可能返回null的方法用@Nullable注釋臣樱。

如果您使用Kotlin聲明存儲(chǔ)庫接口靶擦,則可以使用Kotlin的空安全來表示可空性。

示例38.聲明Kotlin中參數(shù)和返回值的可空性

interface UserRepository : Repository<User, String> {

  fun findByLastname(username: String?): List<User>

  fun findByFirstnameAndLastname(firstname: String, lastname: String): User?
}

Kotlin代碼對(duì)字節(jié)碼進(jìn)行編譯雇毫,該字節(jié)碼不能使用方法簽名表示可空性聲明玄捕,而不是對(duì)元數(shù)據(jù)編譯。 確保包含kotlin反射棚放,以便啟用Kotlin的可空性聲明枚粘。

3.9 Spring Data 擴(kuò)展

本節(jié)介紹了一組Spring數(shù)據(jù)擴(kuò)展,可在各種上下文中啟用Spring數(shù)據(jù)使用飘蚯。 目前大部分的集成針對(duì)Spring MVC馍迄。

3.9.1 Querydsl擴(kuò)展

Querydsl是一個(gè)框架,可以通過流暢的API構(gòu)建靜態(tài)類型的類SQL查詢局骤。

有若干個(gè)Spring數(shù)據(jù)模塊通過QueryDslPredicateExecutor提供與Querydsl的集成攀圈。

示例39. QueryDslPredicateExecutor接口

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

? 查找并返回與 predicate 匹配的單個(gè)實(shí)體。
? 查找并返回與 predicate 匹配的所有實(shí)體峦甩。
? 返回與 predicate 匹配的實(shí)體數(shù)赘来。
? 如果與謂詞匹配的實(shí)體存在,則返回凯傲。

要使用Querydsl支持犬辰,只需在您的存儲(chǔ)庫接口上擴(kuò)展QueryDslPredicateExecutor。

示例40.在存儲(chǔ)庫上進(jìn)行Querydsl集成

interface UserRepository extends CrudRepository<User, Long>, QueryDslPredicateExecutor<User> {

}

以上使用Querydsl Predicate可以編寫類型安全的查詢冰单。

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

userRepository.findAll(predicate);

~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末幌缝,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子诫欠,更是在濱河造成了極大的恐慌狮腿,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,080評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呕诉,死亡現(xiàn)場(chǎng)離奇詭異缘厢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)甩挫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門贴硫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事英遭〖浠ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 157,630評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵挖诸,是天一觀的道長汁尺。 經(jīng)常有香客問我,道長多律,這世上最難降的妖魔是什么痴突? 我笑而不...
    開封第一講書人閱讀 56,554評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮狼荞,結(jié)果婚禮上辽装,老公的妹妹穿的比我還像新娘。我一直安慰自己相味,他們只是感情好拾积,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,662評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著丰涉,像睡著了一般拓巧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上一死,一...
    開封第一講書人閱讀 49,856評(píng)論 1 290
  • 那天肛度,我揣著相機(jī)與錄音,去河邊找鬼摘符。 笑死贤斜,一個(gè)胖子當(dāng)著我的面吹牛策吠,可吹牛的內(nèi)容都是我干的逛裤。 我是一名探鬼主播,決...
    沈念sama閱讀 39,014評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼猴抹,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼带族!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蟀给,我...
    開封第一講書人閱讀 37,752評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤蝙砌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后跋理,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體择克,經(jīng)...
    沈念sama閱讀 44,212評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,541評(píng)論 2 327
  • 正文 我和宋清朗相戀三年前普,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肚邢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,687評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖骡湖,靈堂內(nèi)的尸體忽然破棺而出贱纠,到底是詐尸還是另有隱情,我是刑警寧澤响蕴,帶...
    沈念sama閱讀 34,347評(píng)論 4 331
  • 正文 年R本政府宣布谆焊,位于F島的核電站,受9級(jí)特大地震影響浦夷,放射性物質(zhì)發(fā)生泄漏辖试。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,973評(píng)論 3 315
  • 文/蒙蒙 一军拟、第九天 我趴在偏房一處隱蔽的房頂上張望剃执。 院中可真熱鬧,春花似錦懈息、人聲如沸肾档。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怒见。三九已至,卻和暖如春姑宽,著一層夾襖步出監(jiān)牢的瞬間遣耍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評(píng)論 1 266
  • 我被黑心中介騙來泰國打工炮车, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留舵变,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,406評(píng)論 2 360
  • 正文 我出身青樓瘦穆,卻偏偏與公主長得像纪隙,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子扛或,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,576評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理绵咱,服務(wù)發(fā)現(xiàn),斷路器熙兔,智...
    卡卡羅2017閱讀 134,633評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,773評(píng)論 6 342
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,805評(píng)論 25 707
  • 正午的風(fēng)都去了哪兒 說好了一起 它卻躲的沒影 而我要下地了 卷起褲腿 拿著犁 我的戰(zhàn)士在等著我 我和它 都再不是...
    江小昨閱讀 265評(píng)論 0 2
  • 我的近期的財(cái)富目標(biāo)是盛川科技有限公司收到200萬元的訂單悲伶,貨款到賬
    zdzfk602閱讀 68評(píng)論 0 0