1. 程式人生 > >Spring-data-jpa 筆記(二) Repository 詳解

Spring-data-jpa 筆記(二) Repository 詳解

方法參數 count 有一個 long 合規 page byname mirror id屬性

  

基礎的 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 查詢相關的方法 。

在使用SpringData時只需要定義Dao層接口及定義方法就可以操作數據庫。但是,這個Dao層接口中的方法也是有定義規範的,只有按這個規範來,SpringData才能識別並實現該方法。下面來說說方法定義的規範。

(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)查詢方法解析流程

。這裏再介紹下查詢方法的解析的流程吧,掌握了這個流程,對於定義方法有更深的理解。 <1> 方法參數不帶特殊參數的查詢 假如創建如下的查詢: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()"。
<2> 方法參數帶特殊參數的查詢 特殊的參數: 還可以直接在方法的參數上加入分頁或排序的參數,比如: Page<UserModel> findByName(String name, Pageable pageable) List<UserModel> findByName(String name, Sort sort);

@Query註解

我們在dao層接口按照規則來定義方法就可以不用寫方法的實現也能操作數據庫。但是如果一個條件查詢有多個條件時,寫出來的方法名字就太長了,所以我們就想著不按規則來定義方法名。我們可以使用@Query這個註解來實現這個功能,在定義的方法上加上@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)索引參數和命名參數

在寫jpql語句時,查詢條件的參數的表示有以下2種方式:
  • 索引參數方式如下圖所示,索引值從1開始,查詢中?x‘的個數要和方法的參數個數一致,且順序也要一致
  • 技術分享圖片

  • 命名參數方式(推薦使用這種方式)如下圖所示,可以用‘:參數名‘的形式,在方法參數中使用@Param("參數名")註解,這樣就可以不用按順序來定義形參

技術分享圖片

一個特殊情況,那就是自定義的Query查詢中jpql語句有like查詢時,可以直接把%號寫在參數的前後,這樣傳參數就不用把%號拼接進去了。使用案例如下,調用該方法時傳遞的參數直接傳就ok。

技術分享圖片

(3)使用@Query來指定使用本地SQL查詢
如果你不熟悉jpql語句,你也可以寫sql語句查詢,只需要在@Query註解中設置nativeQuery=true。直接來看案例吧
  • dao層接口寫法如下圖所示
  技術分享圖片  

@Modifying註解和事務

1)@Modifying註解的使用
@Query與@Modifying這兩個註解一起使用時,可實現個性化更新操作及刪除操作;例如只涉及某些字段更新時最為常見。 下面演示一個案例,把id小於3的person的name都改為‘admin‘
  • 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);



使用@[email protected]時的註意事項
  • 方法返回值是int,表示影響的行數
  • 在調用的地方必須加事務,沒事務不執行
(2)事務
  • Spring Data 提供了默認的事務處理方式,即所有的查詢均聲明為只讀事務。
  • 對於自定義的方法,如需改變 Spring Data 提供的事務默認方式,可以在方法上註解 @Transactional 聲明
  • 進行多個 Repository 操作時,也應該使它們在同一個事務中處理,按照分層架構的思想,這部分屬於業務邏輯層,因此,需要在 Service 層實現對多個 Repository 的調用,並在相應的方法上聲明事務。

本文參考博客:http://www.cnblogs.com/zeng1994/

Spring-data-jpa 筆記(二) Repository 詳解