使用Spring Data 倉庫工作 4.1-4.3
Spring Data 倉庫抽象的目標是為了明顯減少為了各種持久儲存的來實現的資料訪問層的樣板程式碼量。
Spring Data儲存庫文件和你的模組
本章解釋了Spring Data 儲存庫的核心觀念,以及介面。本章的資訊來自Spring Data公共模組。它使用了Java Persistence API(JPA)中的配置以及程式碼例項。將名稱空間宣告和要擴充套件的型別擴充套件為你將會使用的模組的等效項。名稱空間引用包含了所有被Spring Data模組支援的儲存庫API的XML配置,儲存庫查詢關鍵字包含了常用的儲存庫抽象所支援的查詢方法關鍵字。對於模組特定特性的詳細資訊,參閱文件中講述該模組的章節。
4.1 核心觀點
在Spring Data 抽象中中心介面就是Repository(沒有多大意外)。它使用域類來管理以及將域類的id型別作為型別引數。這個介面主要作為標記的介面來捕捉工作的型別並且幫助你去發現被擴充套件的介面。CrudRepository 提供了複雜的CRUD功能給以及被管理的實體類。
public interface CrudRepository<T, ID extends Serializable>extends Repository<T, ID> { <S extends T> S save(S entity); //1 T findOne(ID primaryKey); //2 Iterable<T> findAll(); //3 Long count(); //4 void delete(T entity); //5 boolean exists(ID primaryKey); //6 // … more functionality omitted. }
1.儲存實體。
2.返回給定識別符號的實體。
3.返回全部實體。
4.返回實體的個數。
5.刪除給定的實體。
6.表明給定ID的實體是否存在。
我們仍然提供技術指定的持久層抽象,比如JpaRepository 或者 MongoRepository。這些介面擴充套件了CrudRepository 並且比起通用的持久層與技術無關的介面比如CrudRepository來說暴露了另外的底層的持久層技術功能。
在CrudRepository上層有一個PagingAndSortingRepository抽象添加了另外的方法來輕鬆使用分頁的方式訪問實體。
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> { Iterable<T> findAll(Sort sort); Page<T> findAll(Pageable pageable); }
訪問第二頁的頁面大小為20的User,你可以這麼處理:
PagingAndSortingRepository<User, Long> repository = // … get access to a bean
Page<User> users = repository.findAll(new PageRequest(1, 20));
除了查詢方法,查詢匯出計數和刪除查詢都是可用的。
例子3:查詢匯出計數
public interface UserRepository extends CrudRepository<User, Long> {
Long countByLastname(String lastname);
}
例子4 查詢刪除
public interface UserRepository extends CrudRepository<User, Long> {
Long deleteByLastname(String lastname);
List<User> removeByLastname(String lastname);
}
4.2 查詢方法
標準的CRUD功能性儲存庫通常在底層的資料庫裡有查詢。在SpringData 中宣告這些查詢通常有四步:
1.宣告一個擴充套件了Respository或者它的子介面並且設定了它的域類和Id型別的介面。
interface PersonRepository extends Repository<Person, Long> { … }
2.宣告這個介面的查詢方法
interface PersonRepository extends Repository<Person, Long> {
List<Person> findByLastname(String lastname);
}
3.設定Spring為這些介面建立代理例項.可以用JavaConfig
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的名稱空間。如果你要使用其他的儲存庫抽象,你需要替換jpa為合適的儲存模組的名稱空間宣告比如mongodb。
同時,注意JavaConfig變數不會顯示配置包而是預設使用註釋類的包。使用資料儲存特定的儲存庫@Enable…-註解的basePackage屬性來自定義要掃描的包。
4.注入儲存庫的例項並且使用它:
public class SomeClient {
@Autowired
private PersonRepository repository;
public void doSomething() {
List<Person> persons = repository.findByLastname("Matthews");
}
}
接下來的章節解釋了每一步的細節。
4.3 定義儲存庫介面
作為第一步你定義一個域指定類儲存庫介面。這個介面必須擴充套件自Repository,並鍵入到域類和ID型別。如果你想要暴露CRUD方法給域型別可以擴充套件CrudRepository而不是Repository。
###4.3.1 微調儲存庫的定義
一般來說你的儲存庫介面會擴充套件自Repository,CrudRepository或者PagingAndSortingRepository。但是如果你不想要擴充套件Spring Data的介面,你可以通過使用@RepositoryDefinition注入你自己的儲存庫介面。擴充套件CrudRepository 暴露了一系列完整的方法來操控你的實體。如果你偏向於對與暴露的方法有選擇性,簡單的從CrudRepository複製對應的方法到你的域儲存庫中。
這個允許你在提供的Spring Data 儲存庫的功能之上定義你自己的抽象
例子5.選擇性暴露CRUD方法
@NoRepositoryBean
interface MyBaseRepository<T, ID extends Serializable> extends Repository<T, ID> {
T findOne(ID id);
T save(T entity);
}
interface UserRepository extends MyBaseRepository<User, Long> {
User findByEmailAddress(EmailAddress emailAddress);
}
在第一步你定義了一個基本的介面給你全部的域儲存同時暴露了findOne()和Save()兩個方法。這些方法將會被路由到你選擇的由SpringData提供的儲存的基礎的儲存庫實現,比如如果是JPA 就是SimpleJpaRepository,因為他們滿足在CrudRepository中的方法標籤。所以UserRepository現在可以儲存使用者,根據id來查詢指定使用者,或者觸發一個按照他們的郵件地址的查詢。
注意中間的儲存庫介面由@NoRepositoryBean註解表示。確保你添加了這個註解給所有的儲存庫介面,這樣Spring Data不會在執行時建立例項。
4.4.2 對多個Spring Data模組使用儲存庫
在應用中使用一個唯一的Spring Data 模組可以使事情簡單,因此所有在定義範圍內的儲存庫介面都被繫結到Spring Data 模組。有時應用要求使用一個以上的Spring Data 模組。在這個情況下,要求儲存庫的定義可以分辨不同的儲存層技術。由於可以檢測到多個儲存庫工廠在類路徑中,Spring Data 使用嚴格的儲存庫配置模式。嚴格的配置需要儲存庫的細節或者域類來決定Spring Data 模組繫結一個儲存庫的定義:
1.如果儲存庫定義擴充套件指定模組的儲存庫,那麼它是一個合格的對於特定的Spring Data 模組的候選者。
2.如果域類是由模組特定的型別註解來註解的,那麼它是一個合格的對於特定的Spring Data 模組的候選者。Spring Data 模組接受第三方的註解(比如JPA的 @Entity)或者提供自己的註解比如@Document用於Spring Data MongoDB/Spring Data Elasticsearch。
例子6 使用指定模組的介面定義的儲存庫
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模組的合法的候選人。
例子7 使用通用介面定義的儲存庫
interface AmbiguousRepository extends Repository<User, Long> {
…
}
@NoRepositoryBean
interface MyBaseRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
…
}
interface AmbiguousUserRepository extends MyBaseRepository<User, Long> {
…
}
AmbiguousRepository和AmbiguousUserRepository只擴充套件了Repository和CrudRepository在它們的型別層級中。雖然這是完全正確的使用一個唯一的Spring Data模組,多模組沒法辨識這個儲存庫應該繫結哪一個特定的Spring Data。
例子8 使用註解域類定義的儲存庫
interface PersonRepository extends Repository<Person, Long> {
…
}
@Entity
public class Person {
…
}
interface UserRepository extends Repository<User, Long> {
…
}
@Document
public class User {
…
}
由於PersonRepository 引用使用JPA註解@Entity註解的Person,這個儲存庫清楚的屬於Spring Data JPA。UserRepository使用Spring Data MongoDB的@Document註解的User類。
例子9 使用註解和域類混合的儲存庫定義
interface JpaPersonRepository extends Repository<Person, Long> {
…
}
interface MongoDBPersonRepository extends Repository<Person, Long> {
…
}
@Entity
@Document
public class Person {
…
}
這個例子展示一個域類使用了JPA和MongoDb的註解。它定義了兩個儲存庫,JpaPersonRepository和MongoDBPersonRepository.一個給JPA,一個給MongoDB使用。Spring Data 不能分辨出儲存庫,將會導致未定義的行為。
儲存庫型別細節和分辨域類註解用於嚴格的儲存庫配置對一個Spring Data 模組來區分儲存庫候選人。在相同的域型別中使用多個持久層技術註解可以重用域型別來訪問多持久層技術,但是Spring Data 不再可以決定一個特定的模組繫結到儲存庫。
最後一個方法來區分儲存庫是作用域基礎包。基礎包定義了開始點用來掃描哪些儲存庫介面在合適的包中實現了儲存庫的定義。預設的註解驅動配置使用了配置類的包。這個使用XML基礎配置的基礎包是強制性的。
例子10 基礎包的註解驅動配置
@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
interface Configuration { }