這篇文章是在看Spring Data Commons文檔的時(shí)候梳理的內(nèi)容,都是與Spring Data相關(guān)的,里面可能會涉及到Spring Data JPA的內(nèi)容,但更多的是Commons的內(nèi)容,Spring Data JPA只是一個(gè)具體的實(shí)現(xiàn)而已。
1. Spring Data 模塊關(guān)系
Spring Data家族有多個(gè)模塊:
- Spring Data JPA
- Spring Data Mongo
- ....
但所有的模塊都基于Spring Data Commoms
模塊進(jìn)行擴(kuò)展吓歇。例如Spring Data JPA
是針對JPA做擴(kuò)展的一個(gè)子模塊;Spring Data Mongo
是針對MongoDB的子模塊票腰,但是共同的接口都是Spring Data城看。
可以說Spring Data Commons
是其所有子模塊的抽象,定義了一系列的操作標(biāo)準(zhǔn)及接口杏慰。
2. 獨(dú)立使用Spring Data
Spring Data
提供了RepositoryFactory
测柠,可以獨(dú)立于Spring容器之外使用:
RepositoryFactorySupport factory = … // Instantiate factory here
UserRepository repository = factory.getRepository(UserRepository.class);
3. Repository - dao - 存儲倉庫
在Spring Data
中,都是基于存儲倉庫Repository
對實(shí)體類對象進(jìn)行CRUD
操作的逃默。
其中鹃愤,最核心的是Repository
接口,在org.springframework.data.repository
包中定義完域。
Repository
接口需要指定操作的實(shí)體類的類型
软吐、實(shí)體類ID的類型
。在該接口中吟税,是沒有任何方法的凹耙,只是用于標(biāo)記讓Spring Data知道姿现。
通常我們會使用CurdRepository
這個(gè)接口,當(dāng)然肖抱,這個(gè)接口也是不包含JPA特性的备典,我們需要使用JPA特性的話,一般用的是JpaRepository
接口意述。
下面給出Spring Data JPA中提佣,JpaRepository
的繼承結(jié)構(gòu):
4. 使用指定Spring Data模塊
上面一開始說了Spring Data有很多個(gè)不同的子模塊,每個(gè)子模塊對應(yīng)一種數(shù)據(jù)存儲方式或數(shù)據(jù)源荤崇,統(tǒng)稱Spring Data *
吧拌屏。
要讓Spring Data在運(yùn)行的時(shí)候知道我們使用哪個(gè)模塊,就要進(jìn)行指定术荤,指定模塊的方法有兩種:
-
存儲庫
使用Spring Data模塊特定的接口
指定類型 -
實(shí)體類
使用Spring Data模塊模塊特定的注解
指定類型
如果都不指定倚喂,在單模塊的情形下是不存在問題的,但是如果項(xiàng)目中引入了不同的Spring Data模塊瓣戚,那么Spring Data在實(shí)際運(yùn)行中就無法確定到底需要使用哪個(gè)模塊端圈。
5. 查詢方法的拆分
Spring Data
以方法中的結(jié)構(gòu)為findBy...
、deleteBy...
子库,其結(jié)構(gòu)都是動詞加上By
舱权,By
是整個(gè)語句拆分的關(guān)鍵點(diǎn)。通常By
后面跟著的是查詢的參數(shù)
列表刚照,通過And
刑巧、Or
來連接。
在By
后面的參數(shù)中无畔,首先將整個(gè)內(nèi)容作為一個(gè)屬性,如果找不到吠冤,然后再參數(shù)中以大寫字母
為分割浑彰,直到找到對應(yīng)的參數(shù)為止。
6. 分頁查詢拯辙、排序查詢郭变、限制查詢、流式結(jié)果查詢
6.1 排序查詢
Spring Data
接受使用Sort
類型參數(shù)來進(jìn)行排序查詢的工作涯保,Sort
主要有兩個(gè)參數(shù)構(gòu)成:
- 排序方向:ASC(升序)诉濒、DESC(降序)
- 排序字段
6.2 分頁查詢參數(shù)
Pageable
是Spring Data
提供出來進(jìn)行分頁查詢參數(shù)輸入的接口,里面主要定義多個(gè)與頁面設(shè)定的方法:
實(shí)現(xiàn)有很多夕春,最常用的是PageRequest
:
PageRequest
廢棄了原本的new
方式來構(gòu)建分頁參數(shù)未荒,建議使用類中提供的of(...)
靜態(tài)方法來創(chuàng)建相關(guān)的分頁參數(shù)實(shí)體。
of(...)
分頁查詢中允許帶上排序字段
以及排序方向
兩個(gè)參數(shù)及志,在源碼中片排,這兩個(gè)共同構(gòu)建成了Sort
實(shí)體寨腔。這是很多業(yè)務(wù)中需要使用上的,首先需要講內(nèi)容進(jìn)行排序率寡,然后再分頁列出迫卢。
分頁查詢中,next()
冶共、previous()
返回的分別是下一頁
乾蛤、上一頁
的分頁參數(shù)實(shí)體,
6.3 分頁查詢結(jié)果(返回值)
除了支持常用的集合List
捅僵、Set
等查詢結(jié)果集作為分頁查詢結(jié)果幻捏,Spring Data還有以下幾種結(jié)果:
Page<T>
Slice<T>
List<T>
Set<T>
6.4 limit查詢
在Mysql
中,我們需要獲得前N
個(gè)結(jié)果命咐,使用limit
關(guān)鍵字篡九。
在Spring Data
中可以使用:
top
first
來進(jìn)行限定最終的結(jié)果數(shù),在top
醋奠、first
關(guān)鍵字后面可以加上數(shù)字表示最大結(jié)果大小榛臼,默認(rèn)值為1
:
User findFirstByUsername(String username)
List<Article> findFirst10Bytitle(String title)
當(dāng)然,這個(gè)是支持使用Pageable
窜司、Sort
的沛善。
6.5 流式查詢結(jié)果
Spring Data
支持使用Stram<T>
流式API使用。
@Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();
Stream<User> readAllByFirstnameNotNull();
@Query("select u from User u")
Stream<User> streamAllPaged(Pageable pageable);
一般在try-with-resources
中使用:
try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) {
stream.forEach(…);
}
7. 自定義實(shí)現(xiàn)dao存儲庫
7.1 自定義存儲庫片段
都知道當(dāng)使用Spring Data
之后塞祈,大部分的CRUD
操作我們都不需要去實(shí)現(xiàn)金刁。但是當(dāng)某些情況下,例如有些查詢方法需要不同的行為或者無法通過Spring查詢實(shí)現(xiàn)時(shí)议薪,我們確實(shí)是要自己手動實(shí)現(xiàn)查詢方法的尤蛮。這種做法叫Repository fragments
,存儲庫片段斯议。
步驟如下:
- 做出一個(gè)自己的接口:
interface CustomizedUserRepository {
void someCustomMethod(User user);
}
- 實(shí)現(xiàn)接口产捞,就是很普通的接口實(shí)現(xiàn)
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {
public void someCustomMethod(User user) {
// Your custom implementation
}
}
注:
- 上面的實(shí)現(xiàn)中,一定要以
Impl
為結(jié)尾- 接口的實(shí)現(xiàn)可以作為一個(gè)普通的
Bean
存在
- 讓
*Repository
存儲庫擴(kuò)展(繼承)這個(gè)接口:
interface UserRepository extends CrudRepository<User, Long>, CustomizedUserRepository {
// Declare query methods here
}
注:
當(dāng)有兩個(gè)片段提供相同的方法和簽名哼御,那么將按照繼承順序覆蓋坯临。
但是別忘了,每個(gè)片段的實(shí)現(xiàn)
上面這種做法可以組合Spring Data
提供的CURD
以及自定義實(shí)現(xiàn)的接口恋昼。相對自由看靠,但是也相對復(fù)雜。
注:
而且對于領(lǐng)域設(shè)計(jì)
的角度來說液肌,能不把業(yè)務(wù)放到dao
層就千萬不要這么做P妗!
7.2 使用命名空間來配置自定義存儲庫片段Bean
在XML
和Java Config
中的配置base-package
,Spring Data
的基礎(chǔ)架構(gòu)會在啟動的時(shí)候到配置的路徑下去掃描對應(yīng)的存儲庫包辟宗,找到實(shí)現(xiàn)并配置為Bean
爵赵。
因此,在上面說了實(shí)現(xiàn)片段需要后綴為Impl
泊脐。
如果不是空幻,可以通過repository-impl-postfix
來配置對應(yīng)的后綴,如:
<repositories base-package="com.acme.repository" repository-impl-postfix="MyPostfix" />
如果是使用Java Config:
@EnableJpaRepositories(basePackages = "cn.marer", repositoryImplementationPostfix = "DAO")
public class ....
后綴是可以配置多個(gè)的容客,不同的后綴就掃描不同的結(jié)果秕铛。
7.3 自定義BaseRepository - 基類
在Spring Data出現(xiàn)之前我們會使用BaseDao
來實(shí)現(xiàn)一些基本的數(shù)據(jù)庫的操作,現(xiàn)在也可以這樣做:
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
}
}
注:
- 該類需要具有特定于商店的存儲庫工廠實(shí)現(xiàn)所使用的超類的構(gòu)造函數(shù)缩挑。
- 如果存儲庫基類具有多個(gè)構(gòu)造函數(shù)但两,則覆蓋使用EntityInformation加號存儲特定基礎(chǔ)結(jié)構(gòu)對象(例如,EntityManager模板類)的構(gòu)造函數(shù)供置。
然后還需要在Java Config
中指定存儲庫基類:
@Configuration
@EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class)
class ApplicationConfiguration { … }
這個(gè)就類似于為基類配置一個(gè)Bean谨湘。
8. 發(fā)布事件
直接官方機(jī)翻:
由存儲庫管理的實(shí)體是聚合根。在域驅(qū)動設(shè)計(jì)應(yīng)用程序中芥丧,這些聚合根通常會發(fā)布域事件紧阔。Spring Data提供了一個(gè)注釋@DomainEvents,可以在聚合根的方法上使用续担,以使該發(fā)布盡可能簡單擅耽,如以下示例所示:
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í)例或事件集合。它不能使用任何參數(shù)物遇。
在所有事件發(fā)布后乖仇,我們有一個(gè)注釋的方法@AfterDomainEventPublication
。它可用于潛在地清除要發(fā)布的事件列表(以及其他用途)询兴。
9. Spring Data對Spring MVC支持
9.1 打開Spring data對web的支持
Spring Data提供了對web的友好支持乃沙,特別是下面將說到的領(lǐng)域類型(可以先看作實(shí)體類)轉(zhuǎn)換
的支持以及分頁、排序
的支持蕉朵,要打開支持崔涂,有兩種方式:
- Java Config
- XML
還是比較推薦使用Java Config的方式,畢竟Spring Boot大多數(shù)都是注解形式嘛:
@EnableSpringDataWebSupport
piublic class WebConfiguration {
...
}
XML開啟:
<bean class="org.springframework.data.web.config.SpringDataWebConfiguration" />
按照官方文檔始衅,如果打開了這個(gè)支持,Spring會自動配置了下面三個(gè)Bean:
DomainClassConverter
- 允許在不用通過存儲庫手動查找的情況下缭保,直接在SpringMVC
的控制器方法的簽名(參數(shù))中直接使用領(lǐng)域?qū)ο?/p>PageableHandlerMethodArgumentResolver
- 允許通過請求參數(shù)
將Pageable
對象注入到controller方法參數(shù) Pageable
中SortHandlerMethodArgumentResolver
- 允許通過請求參數(shù)
將Sort
對象注入到controller方法參數(shù) Sort
中
9.2 DomainClassConverter - 領(lǐng)域類型轉(zhuǎn)換器支持
DomainClassConverter
可以在不用通過存儲庫手動查找的情況下汛闸,直接在SpringMVC的控制器方法的簽名(參數(shù))中直接使用領(lǐng)域?qū)ο蟆?br>
需要按照上面的兩種方式其中一個(gè)打開對web的支持。
現(xiàn)在假定有User
這個(gè)領(lǐng)域?qū)ο蠛?code>UserRepository這個(gè)存儲庫艺骂,那么可以直接使用:
@RestController
public class UserController {
@PostMapping("/user/info/{id}")
public User getUserInfo(@PathVariable("id") User user) {
logger.info("Get user info By Spring data web support");
return user;
}
}
上面方法中诸老,并沒有通過UserRepository
存儲庫對象來獲取相對應(yīng)的用戶數(shù)據(jù)。
SpringMVC將提交上來的參數(shù){id}
通過@PathVariable
獲得ID钳恕,然后調(diào)用findById(...)
查找出對應(yīng)的實(shí)體并注入到方法參數(shù)中别伏。所以可以直接通過返回User
參數(shù)蹄衷,算是偷懶方式。
注意:
- 領(lǐng)域類型對應(yīng)的存儲庫最起碼是要實(shí)現(xiàn)
CurdRepository
才可以實(shí)現(xiàn)這個(gè)功能厘肮,- 如果找不到數(shù)據(jù)愧口,會返回
500 Internal Server Error
,需要對數(shù)據(jù)做校驗(yàn)或者做異常處理类茂。
9.3 HandlerMethodArgumentResolvers - 分頁耍属、排序的支持
在上面說了,需要讓Spring Data支持分頁或排序功能巩检,就需要在存儲庫Repository
接口方法中傳入Pageable
或Sort
對象厚骗,Pageable
分頁的對象里面也包含了Sort
排序。Spring Data會自動解析并對分頁兢哭、排序進(jìn)行limit
领舰、order by
查詢。
而Spring Data也對web提供了分頁迟螺、排序的支持冲秽,讓我們可以在訪問http提交請求參數(shù)的時(shí)候直接將pageable
、sort
參數(shù)注入到controller的方法參數(shù)煮仇。
主要是配置了@EnableSpringDataWebSupport
后劳跃,Spring Data會生成PageableHandlerMethodArgumentResolver
、SortHandlerMethodArgumentResolver
兩個(gè)Bean實(shí)例浙垫。在我們的Controller的方法中加入?yún)?shù)即可:
@RestController
public class UserController {
@GetMapping("/user/info/all/page")
public List<User> getAllUserPage(Pageable pageable){
logger.info(“try to find user with pageable.”);
return userRepository.findAll(pageable).getContent();
}
}
在上面的例子中刨仑,要做的是:分頁顯示用戶。使用了Pageable
作為例子夹姥,因?yàn)槲覀冎?code>Pageable里面包含了Sort
杉武,因此Sort
的例子在這里就不再敘述了。
上面的例子里面辙售,提交的參數(shù)為:
- page - 第幾頁轻抱,默認(rèn)為0
- size - 頁面數(shù)據(jù)數(shù)量,默認(rèn)為20
- sort - 排序方向旦部,默認(rèn)為升序
ASC
假設(shè):查詢第一頁的用戶并根據(jù)用戶名降序排序祈搜,頁面大小為15。則請求API如下:
/user/info/all/page?page=0&size=15&sort=username,DESC
注意:
上面這個(gè)例子是GET
請求方法的士八,如果是POST
請求方法容燕,請把請求參數(shù)放到請求體里面
更多關(guān)于Spring Data對Web支持請移步到:
https://docs.spring.io/spring-data/jpa/docs/2.1.3.RELEASE/reference/html/#core.web
此文同時(shí)在簡書發(fā)布:http://www.reibang.com/p/cb5a3ab2727e
此文同時(shí)在CSDN發(fā)布:https://blog.csdn.net/nthack5730/article/details/84939027
轉(zhuǎn)載要加原文鏈接!謝謝支持婚度!