Spring Data JPA
首先,讓IPersonDAO接口繼承Repository<T, ID extends Serializable>接口
public interface IPersonDao extends Repository<Person,Long> {
void save(Person person);
//按照SpringDataJPA的查詢方法命名規(guī)范定義查詢方法
Person findById(Long id);
}
其次,在applicationContext.xml中做如下配置
<jpa:repositories base-package="cn.wolfcode._02_spring_data_jpa" entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager"/>
最后,執(zhí)行對findById方法的測試,結果如下
Hibernate: select person0_.id as id1_0_, person0_.age as age2_0_, person0_.name as name3_0_
from Person person0_ where person0_.id=?
Person(id=1, name=Neld, age=10)
我們發(fā)現(xiàn),在并沒有編寫dao的實現(xiàn)類的情況下,仍然完成了數(shù)據(jù)的查詢操作,那么Spring Data JPA是如何完成的呢?接下來分析分析
- applicationContext.xml中配置jps:repositories,作用是對指定包中的持久化對象(實現(xiàn)了Repositories接口的類)進行掃描,然后為其生成代理對象(實現(xiàn)該接口的類)
- 在代理對象中,框架怎么知道我們要指定什么sql呢?
很簡單,因為我們繼承的是一個泛型接口,在接口中指定了要操作的實體類型和主鍵的類型,再加上我們在持久化類上有通過注解對對象和關系進行映射,
最后,再通過解析crud方法名稱,獲取到我們的需求,如:
findById(Long id):我們所有查詢相關的方法都是以findBy開頭,然后按規(guī)范跟上where子句中的條件即可
這樣,框架就知道該方法是要根據(jù)什么樣的條件查詢什么樣的數(shù)據(jù)了
方法的命名規(guī)范如下圖所示:
也就是說,使用Spring Data JPA,我們只需要在dao中按照上面的方法命名規(guī)范定義方法,框架就能夠自動為我們生成對應的SQL語句了,如:
在創(chuàng)建查詢時,我們通過在方法名中使用屬性名稱來表達,比如 findByUserAddressZip ()。框架在解析該方法時,首先剔除 findBy鸵膏,然后對剩下的屬性進行解析钦勘,詳細規(guī)則如下(此處假設該方法針對的域對象為 AccountInfo 類型):
先判斷 userAddressZip (根據(jù) POJO 規(guī)范柿冲,首字母變?yōu)樾懀峦┦欠駷?AccountInfo 的一個屬性堵第,如果是吧凉,則表示根據(jù)該屬性進行查詢;如果沒有該屬性踏志,繼續(xù)第二步阀捅;
從右往左截取第一個大寫字母開頭的字符串(此處為 Zip),然后檢查剩下的字符串是否為 AccountInfo 的一個屬性针余,如果是饲鄙,則表示根據(jù)該屬性進行查詢;如果沒有該屬性圆雁,則重復第二步忍级,繼續(xù)從右往左截取伪朽;最后假設 user 為 AccountInfo 的一個屬性轴咱;
接著處理剩下部分( AddressZip ),先判斷 user 所對應的類型是否有 addressZip 屬性烈涮,如果有朴肺,則表示該方法最終是根據(jù) “AccountInfo.user.addressZip” 的取值進行查詢;否則繼續(xù)按照步驟 2 的規(guī)則從右往左截取坚洽,最終表示根據(jù) “AccountInfo.user.address.zip” 的值進行查詢戈稿。
可能會存在一種特殊情況,比如 AccountInfo 包含一個 user 的屬性讶舰,也有一個 userAddress 屬性鞍盗,此時會存在混淆。讀者可以明確在屬性之間加上 “_” 以顯式表達意圖跳昼,比如 “findByUser_AddressZip()” 或者 “findByUserAddress_Zip()”般甲。
Spring Data JPA之核心接口分析
在上面,我們把Spring Data JPA實現(xiàn)CRUD的基本原理做了一個簡單的分析,在了解了這些之后,我們再來看看Spring Data JPA中為我們提供的一系列接口
Repository<T, ID extends Serializable>
這是Spring Data JPA中最核心的接口,該接口是一個標識接口,沒有定義任何方法,繼承了該接口的接口,都應該被掃描生成對應的代理對象
CrudRepository<T, ID extends Serializable> extends Repository<T, ID>
這是Spring Data JPA為開發(fā)者提供的一個完成CRUD功能必需的用到的方法的接口,如下:
@NoRepositoryBean//說明這不屬于持久層的JavaBean,不需要為其生成代理對象
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
<S extends T> S save(S var1);
<S extends T> Iterable<S> save(Iterable<S> var1);
T findOne(ID var1);
boolean exists(ID var1);
Iterable<T> findAll();
Iterable<T> findAll(Iterable<ID> var1);
long count();
void delete(ID var1);
void delete(T var1);
void delete(Iterable<? extends T> var1);
void deleteAll();
}
通過方法名稱,相信大家能夠快速的理解每個方法的作用,都是我們CRUD中的常用方法
如果開發(fā)者有這樣的需求,直接讓你的dao繼承該接口即可,那么Spring Data JPA就會為我們實現(xiàn)該這個接口中的所有方法
PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID>
分頁和排序是開發(fā)中的普遍需求,Spring Data JPA也考慮到了,所以專門提供一個接口,在該接口中提供分頁和排序需要使用到的方法
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort var1);
Page<T> findAll(Pageable var1);
}
JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T>
該即可是針對JPA提供的接口,繼承了PagingAndSortingRepository接口,在父接口的基礎上提供了更多的業(yè)務方法,如:saveAndFlush(),deleteInBatch(),flush()等
@NoRepositoryBean
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll();
List<T> findAll(Sort var1);
List<T> findAll(Iterable<ID> var1);
<S extends T> List<S> save(Iterable<S> var1);
void flush();
<S extends T> S saveAndFlush(S var1);
void deleteInBatch(Iterable<T> var1);
void deleteAllInBatch();
T getOne(ID var1);
<S extends T> List<S> findAll(Example<S> var1);
<S extends T> List<S> findAll(Example<S> var1, Sort var2);
}
在實際開發(fā)中,我們可以根據(jù)自身的需求,自行選擇需要繼承的接口,但是,無論怎樣,我們都必須要繼承Repository<T, ID>接口