基礎的 Repository 提供了最基本的數據訪問功能,其幾個子接口則擴展了一些功能。它們的繼承關系如下:
Repository: 是 spring Data 的一個核心接口,它不提供任何方法,開發者需要在自己定義的接口中聲明需要的方法
僅僅是一個標識,表明任何繼承它的均為倉庫接口類,方便Spring自動掃描識別,
@Indexed public interface Repository<T, ID> { }
T :實體類名 ID : 主鍵類型
CrudRepository: 繼承 Repository,實現了一組 CRUD 相關的方法
@NoRepositoryBean public interface CrudRepository<T, ID> extends Repository<T, ID> { <S extends T> S save(S entity); <S extends T> Iterable<S> saveAll(Iterable<S> entities); Optional<T> findById(ID id); boolean existsById(ID id); Iterable<T> findAll(); Iterable<T> findAllById(Iterable<ID> ids); long count(); void deleteById(ID id); void delete(T entity); void deleteAll(Iterable<? extends T> entities); void deleteAll(); }
PagingAndSortingRepository: 繼承 CrudRepository,實現了一組分頁排序相關的方法
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID>
Iterable<T> findAll(Sort sort); Page<T> findAll(Pageable pageable); }
使用舉例:
如果要在以20為一頁的結果中,獲取第2頁結果,則如下使用:
Page<User> users = repository.findAll(new PageRequest(1, 20));
JpaRepository: 繼承 PagingAndSortingRepository,實現一組 JPA 規范相關的方法
自定義的 XxxxRepository 需要繼承 JpaRepository,這樣的 XxxxRepository 接口就具備了通用的數據訪問控制層的能力。
JpaSpecificationExecutor: 不屬於Repository體系,實現一組 JPA Criteria 查詢相關的方法 。
(1)簡單的條件查詢的方法定義規范
- 簡單條件查詢:查詢某一個實體或者集合
- 按照SpringData規范,查詢方法於find|read|get開頭,涉及條件查詢時,條件的屬性用條件關鍵字連接,要注意的是:屬性首字母需要大寫。
- 支持屬性的級聯查詢;若當前類有符合條件的屬性, 則優先使用, 而不使用級聯屬性。 若需要使用級聯屬性, 則屬性之間使用 _ 進行連接。
(2)支持的關鍵字
直接在接口中定義方法,如果符合規范,則不用寫實現。目前支持的關鍵字寫法如下:
(3)屬性級聯查詢的案例
- 修改Person類,添加address屬性,使Person和Address成多對一的關系,設置外鍵列名為address_id ,添加的代碼如下圖
- 在PersonDao接口中定義一個方法,代碼如下:
// 級聯查詢,查詢address的id等於條件值/ List<Person> findByAddressId(Integer addressId);
運行測試方法
/** 測試findByAddressId方法 */ @Test PersonDao personDao = ctx.getBean(PersonDao.class); // 查出地址id為1的person集合 List<Person> list = personDao.findByAddressId(1); for (Person person : list) { System.out.println(person.getName() + "---addressId=" + person.getAddress().getId()); } }
(4)查詢方法解析流程
- 首先剔除 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注解
(1)使用Query結合jpql語句實現自定義查詢
- 在PersonDao接口中聲明方法,放上面加上Query注解,注解里面寫jpql語句,代碼如下:
// 自定義的查詢,直接寫jpql語句; 查詢id<? 或者 名字 like?的person集合 @Query("from Person where id < ?1 or name like ?2") List<Person> testPerson(Integer id, String name); // 自定義查詢之子查詢,直接寫jpql語句; 查詢出id最大的person @Query("from Person where id = (select max(p.id) from Person as p)") Person testSubquery();
(2)索引參數和命名參數
- 索引參數方式如下圖所示,索引值從1開始,查詢中'?x'的個數要和方法的參數個數一致,且順序也要一致
- 命名參數方式(推薦使用這種方式)如下圖所示,可以用':參數名'的形式,在方法參數中使用@Param("參數名")注解,這樣就可以不用按順序來定義形參
一個特殊情況,那就是自定義的Query查詢中jpql語句有like查詢時,可以直接把%號寫在參數的前后,這樣傳參數就不用把%號拼接進去了。使用案例如下,調用該方法時傳遞的參數直接傳就ok。
(3)使用@Query來指定使用本地SQL查詢
- dao層接口寫法如下圖所示

@Modifying注解和事務
1)@Modifying注解的使用
- dao層代碼如下所示
//可以通過自定義的 JPQL 完成 UPDATE 和 DELETE 操作. 注意: JPQL 不支持使用 INSERT //在 @Query 注解中編寫 JPQL 語句, 但必須使用 @Modifying 進行修飾. 以通知 SpringData, 這是一個 UPDATE 或 DELETE 操作 //UPDATE 或 DELETE 操作需要使用事務, 此時需要定義 Service 層. 在 Service 層的方法上添加事務操作. //默認情況下, SpringData 的每個方法上有事務, 但都是一個只讀事務. 他們不能完成修改操作! @Modifying @Query("UPDATE Person p SET p.name = :name WHERE p.id < :id") int updatePersonById(@Param("id")Integer id, @Param("name")String updateName);
- 方法返回值是int,表示影響的行數
- 在調用的地方必須加事務,沒事務不執行
(2)事務
- Spring Data 提供了默認的事務處理方式,即所有的查詢均聲明為只讀事務。
- 對於自定義的方法,如需改變 Spring Data 提供的事務默認方式,可以在方法上注解 @Transactional 聲明
- 進行多個 Repository 操作時,也應該使它們在同一個事務中處理,按照分層架構的思想,這部分屬於業務邏輯層,因此,需要在 Service 層實現對多個 Repository 的調用,並在相應的方法上聲明事務。