Spring Data所解決的問題
- Spring Data :提供了一整套數據訪問層(DAO)的解決方案,致力于減少數據訪問層(DAO)的開發量。它使用一個叫作Repository的接口類為基礎,它被定義為訪問底層數據模型的超級接口。
- 此接口作為標識接口,里面沒有規定任何方法,所有繼承這個接口的interface都被spring所管理。
- 而對于某種具體的數據訪問操作,則在其子接口中定義。
public interface Repository<T, ID extends Serializable> {
}
什么是Repository?
- Spring Data給我們提供幾個Repository,基礎的Repository提供了最基本的數據訪問功能,其幾個子接口則擴展了一些功能。它們的繼承關系如下:
-
Repository: 僅僅是一個標識,表明任何繼承它的均為倉庫接口類,方便Spring自動掃描識別
-
CrudRepository: 繼承Repository,實現了一組CRUD相關的方法 【save() saveAll() delete() deleteById() deleteAll() findById() findAllById() findAll() existsById() count(),以及其他也可能加all的】
-
PagingAndSortingRepository: 繼承CrudRepository,實現了一組分頁、排序相關的方法 【排序輸入排序參數,返回Iterable對象;分頁輸入Pageable對象,返回Page對象】
-
JpaRepository: 繼承PagingAndSortingRepository,實現一組JPA規范相關的方法【方法名類似Crud那個類,但是對于findxxx方法,如果結果是多個的,crud是返回一個iterable,而jpa直接返回一個list】
-
JpaSpecificationExecutor: 比較特殊,不屬于Repository體系,實現一組JPA Criteria查詢相關的方法
-
我們自己定義的XxxxRepository需要繼承JpaRepository,這樣我們的XxxxRepository接口就具備了通用的數據訪問控制層的能力。
注解方式代替繼承Repository
@RepositoryDefinition(domainClass = Employee.class, idClass = Integer.class)
Spring Data可以讓我們只定義接口,只要遵循spring data的規范,就無需寫實現類。
那么,什么是規范?
- 查詢方法以find|read|get開頭
- 涉及條件查詢時,條件的屬性用條件關鍵字連接,要注意的是:條件屬性以首字母大寫其余字母與變量名相同。
解析過程
- Spring Data JPA框架在進行方法名解析時,會先把方法名多余的前綴截取掉,比如 find、findBy、read、readBy、get、getBy,然后對剩下部分進行解析。
- 假如創建如下的查詢:findByUserDepUuid(),框架在解析該方法時,首先剔除 findBy,然后對剩下的屬性進行解析,假設查詢實體為Doc
- 先判斷 userDepUuid (根據 POJO 規范,首字母變為小寫)是否為查詢實體的一個屬性,如果是,則表示根據該屬性進行查詢;如果沒有該屬性,繼續下一步;
- 從右往左截取第一個大寫字母開頭的字符串此處為Uuid),然后檢查剩下的字符串是否為查詢實體的一個屬性,如果是,則表示根據該屬性進行查詢;如果沒有該屬性,則重復上一步,繼續從右往左截??;最后假設user為查詢實體的一個屬性;
- 接著處理剩下部分(DepUuid),先判斷 user 所對應的類型是否有depUuid屬性,如果有,則表示該方法最終是根據 “ Doc.user.depUuid” 的取值進行查詢;否則繼續按照步驟 3 的規則從右往左截取,最終表示根據 “Doc.user.dep.uuid” 的值進行查詢。
- 可能會存在一種特殊情況,比如 Doc包含一個 user 的屬性,也有一個 userDep 屬性,此時會存在混淆??梢悦鞔_在屬性之間加上 "_" 以顯式表達意圖,比如 "findByUser_DepUuid()" 或者 "findByUserDep_uuid()"
@Query
- 直接在方法上添加@query注解,并寫上完整的sql語句
// 查找id最大的員工
@Query("select o from Employee o where id=(select max(id) from Employee t1)")
public Employee getEmployeeByMaxId();
【注意】這里的Employee不是表名,是在Java代碼里聲明的對應表的類名
// 帶占位符的sql語句
@Query("select o from Employee o where o.name=?1 and o.age=?2")
public List<Employee> queryParams1(String name, Integer age);
- 借用@Param注解,用(:具名變量)代替占位符,更好記和表示
@Query("select o from Employee o where o.name=:name and o.age=:age")
public List<Employee> queryParams2(@Param("name")String name, @Param("age")Integer age);
@Query("select o from Employee o where o.name like %?1%")
public List<Employee> queryLike1(String name);
@Query("select o from Employee o where o.name like %:name%")
public List<Employee> queryLike2(@Param("name")String name);
@Query(nativeQuery = true, value = "select count(1) from employee") // count(1)表示計數全部
public long getCount();
【注意】這里的employee是數據表,存在于數據庫中
更新操作需要@query結合@Modifying、@Transactional,其中@Transactional要放在service里,在service里調用下面被注解的update方法,才是完整的操作過程。
@Modifying
@Query("update Employee o set o.age = :age where o.id = :id")
public void update(@Param("id")Integer id, @Param("age")Integer age);