Further study of Spring Data JPA - 深刻學習篇(一)

由於最近項目要選擇使用Spring Data JPA,因此不得不去學習一波,記錄一下學習過程遇到的問題,以此謹記於心。java

前言

Spring Data存儲庫抽象中的中央接口是Repository,Spring Data JPA只是Spring Data中的一個子模塊,JPA是一套標準接口,而Hibernate是JPA的實現,它須要域類以及域類的ID類型做爲類型參數來進行管理。該接口主要用做標記接口,以捕獲要使用的類型並幫助您發現擴展該接口的接口,Spring Data JPA經常使用的接口是JpaRepository,JpaRepository繼承了接口PagingAndSortingRepository和QueryByExampleExecutor,具有較全的方法。正則表達式

什麼是Spring Data JPA?

在這裏我選擇幾個Spring Data JPA比較重要的對象進行介紹和源碼解析,幫助咱們理解:數據庫

QueryByExampleExecutor 支持動態查詢,不支持過濾條件分組,即不支持夠快提哦啊見用or來鏈接,全部的裸鋁條件都是簡單一層的用and鏈接,eg:firstname=?1 or(firstname=?1 and lastname=?2),只支持字符串的開始/包含/結束/正則表達式匹配和其餘屬性類型的精確匹配
PagingAndSortingRepository CrudRepository的子接口,添加分頁和排序的功能
JpaRepository PagingAndSortingRepository的子接口,增長了一些實用的功能,好比:批量操做等。
PageRequest Spring Data JPA的分頁對象,直接用 PageRequest.of這個方法來建立Pageable分頁對象

QueryByExampleExecutor源碼,具體方法做用我已經標註上:框架

public interface QueryByExampleExecutor<T> {
    //根據實例查看一個對象
    <S extends T> Optional<S> findOne(Example<S> var1);
    //根據實例查找一批對象
    <S extends T> Iterable<S> findAll(Example<S> var1);
    //根據實例查找一批對象,且排序
    <S extends T> Iterable<S> findAll(Example<S> var1, Sort var2);
    //根據實例查找一批對象,排序且分頁
    <S extends T> Page<S> findAll(Example<S> var1, Pageable var2);
    //根據實例查找返回符合條的件對象個數
    <S extends T> long count(Example<S> var1);
    //根據實例判斷是否有符合條件的對象
    <S extends T> boolean exists(Example<S> var1);
}

說以咱們看Example基本上就能夠掌握它的用法和API了,Example接口:源碼分析

public interface Example<T> {

    static <T> Example<T> of(T probe) {
        return new TypedExample(probe, ExampleMatcher.matching());
    }

    static <T> Example<T> of(T probe, ExampleMatcher matcher) {
        return new TypedExample(probe, matcher);
    }

    T getProbe();

    ExampleMatcher getMatcher();

    default Class<T> getProbeType() {
        return ProxyUtils.getUserClass(this.getProbe().getClass());
    }
}

從源碼咱們能夠看出Example主要包括三部份內容:學習

  • Probe:具備填充字段的域對象和實際實體類,即查詢條的封裝類,在持久層框架與Table的對應的與對象。
  • ExampleMatcher:匹配器,ExampleMatcher有關於如何匹配特定字段的匹配規則,能夠重複使用在多個實例,如,要查詢名字"Java"的書籍,即名字以"Java"開頭的書籍,該對象就表示了"以某某開頭的"這個查詢方式,以下面例子:withMatcher("name",ExampleMatcher.GenericPropertyMatchers.startsWith())不填的話,用默認的。
  • Example:實體對象,表明完整的查詢條件,有實體對象(查詢條件值)和匹配器(查詢方式)共同建立,Example由探針和ExampleMatcher組成,用於建立查詢。

QueryByExampleExecutor使用實例:this

Book book= new Book();
book.setName("Java");
book.setAuthor("林必昭");


ExampleMatcher matcher = ExampleMatcher.matching()
            .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.startsWith())   //書籍名稱採用"開始匹配"的方式查詢
            .withMatcher("author",ExampleMatcher.GenericPropertyMatchers.exact())       //書籍做者採用"精確匹配"的方式查詢
            .withIgnorePaths("focus");                          //忽略屬性,是否關注。由於是基本類型,須要忽略掉
    //建立實例
    Example<Book> ex = Example.of(book,matcher);
    
    //查詢
    List<Book> bookList = bookDao.findAll(ex);
    
    //輸出結果
    for(Book book:bookList){
        System.out.println(book.getName());
    }

 

PagingAndSortingRepository源碼,PagingAndSortingRepository接口繼承了CrudRepository接口:spa

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
    //具備排序功能
    Iterable<T> findAll(Sort var1);
    
    //具備分頁功能
    Page<T> findAll(Pageable var1);
}

JpaRepository源碼分析:.net

@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
    //查詢全部
    List<T> findAll();
    
    //查詢全部並排序
    List<T> findAll(Sort var1);

    //根據ID進行查找
    List<T> findAllById(Iterable<ID> var1);

    //保存
    <S extends T> List<S> saveAll(Iterable<S> var1);

    //save不會當即提交到數據庫,flush則是當即提交生次
    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);
}

PageRequest的源碼:code

public class PageRequest extends AbstractPageRequest {
    private static final long serialVersionUID = -4541509938956089562L;
    private final Sort sort;

    /** @deprecated */
    @Deprecated
    public PageRequest(int page, int size) {
        this(page, size, Sort.unsorted());
    }

    /** @deprecated */
    @Deprecated
    public PageRequest(int page, int size, Direction direction, String... properties) {
        this(page, size, Sort.by(direction, properties));
    }

    /** @deprecated */
    @Deprecated
    public PageRequest(int page, int size, Sort sort) {
        super(page, size);
        this.sort = sort;
    }

    public static PageRequest of(int page, int size) {
        return of(page, size, Sort.unsorted());
    }

    public static PageRequest of(int page, int size, Sort sort) {
        return new PageRequest(page, size, sort);
    }

    public static PageRequest of(int page, int size, Direction direction, String... properties) {
        return of(page, size, Sort.by(direction, properties));
    }

    public Sort getSort() {
        return this.sort;
    }

    public Pageable next() {
        return new PageRequest(this.getPageNumber() + 1, this.getPageSize(), this.getSort());
    }

    public PageRequest previous() {
        return this.getPageNumber() == 0 ? this : new PageRequest(this.getPageNumber() - 1, this.getPageSize(), this.getSort());
    }

    public Pageable first() {
        return new PageRequest(0, this.getPageSize(), this.getSort());
    }

    public boolean equals(@Nullable Object obj) {
        if (this == obj) {
            return true;
        } else if (!(obj instanceof PageRequest)) {
            return false;
        } else {
            PageRequest that = (PageRequest)obj;
            return super.equals(that) && this.sort.equals(that.sort);
        }
    }

    public int hashCode() {
        return 31 * super.hashCode() + this.sort.hashCode();
    }

    public String toString() {
        return String.format("Page request [number: %d, size %d, sort: %s]", this.getPageNumber(), this.getPageSize(), this.sort);
    }
}

JPA經常使用註解:

JPA的命名規範:

下面列舉一些注意事項,是在學習中總結出來的,很重要:

  1. ?一、?2表示佔位符,?1表明在方法在參數裏的第一個參數區別於其餘index,這裏是從1開始的。
  2. @Param("變量名")與=:變量名對應匹配的,而不是和實際的方法裏的參數對應。
  3. 要使用原生SQL查詢,須要在@Query註解中設置nativeQuery=true,而後value變動爲原生的SQL一致。
  4. 使用原生的SQL查詢語句對應的字段與數據庫的字段一致,若是是使用JPQL的查詢,對應的查詢語句字段是根實體的一致,好比:c.first_name(原生SQL),c.firstName(JPQL),這裏的first_name和firstName分別對應的是數據庫字段名和Entity實體名。
  5. JPQL的語法中,表名的位置對應Entity的名稱,字段對應Entity的屬性(感受這個重複了,不要緊)

關於JPA的排序的幾種實現方法:

  1. 基於參數排序
  2. 基於自定義的@Query進行排序
  3. 基於Pageable中的Sort字段
  4. 直接建立Sort對象,適合對單一屬性作排序
  5. 經過屬性的List集合建立Sort對象,適合對多個屬性
  6. 經過Sort.Order對象的List集合建立Sort對象

下面我列舉每一種的實現方法來加以說明排序的實現:

1.基於參數排序

//創建分頁對象
Pageable pageable = new PageRequest(PageNum,PageSize);
//在Respository定義方法
Page<Entity>findCustomerByFirstNameOrderByIdAsc(Pageabe pageble);

2.基於自定義的@Query進行排序


@Query("select c from Customer c ORDER BY c.id ASC,c.firstName DESC")
List<Customer> findCustomerByFirstNameOrderByIdAsc();

3.基於Pageable中的Sort字段

Sort sort1 = new Sort(Sort.Direction.ASC,"id");
Pageable pageable = PageRequest.of(pageNum,pageSize,sort1);

//在service調用具體的Repository的方法
Page<Customer> Customers = customerRepository.findAll(pageable);

4.直接建立Sort對象,適合對單一屬性作排序

 Sort sort1 = new Sort(Sort.Direction.ASC,"id");
 List<Customer> customerListAsc = customerRepository.findByName3AndSort("Bauer", sort1);

5.經過屬性的List集合建立Sort對象,適合對多個屬性

List<String> sortProperties = new ArrayList<>();
sortProperties.add("id");
sortProperties.add("firstName");

Sort sort2 = new Sort(Sort.Direction.ASC,sortProperties);
List<Customer> result2 = customerRepository.findByName3AndSort("Bauer", sort2);

6.經過Sort.Order對象的List集合建立Sort對象

 //經過建立Sort.Order對象的集合建立sort對象
        System.out.println("經過建立Sort.Order對象的集合建立sort對象");
        List<Sort.Order> orders = new ArrayList<>();
        orders.add(new Sort.Order(Sort.Direction.DESC,"id"));       //id降序
        orders.add(new Sort.Order(Sort.Direction.ASC,"firstName")); //firstName升序

//        List<Customer> result3 = customerRepository.findByName3AndSort("Bauer", new Sort(orders));
        List<Customer> result3 = customerRepository.findByName3AndSort("Bauer", Sort.by(orders));

對於第六種方法,適合對多個屬性,採用不一樣的排序方式排序,比較實用,JPA已經將public Sort(List<Sort.Order> orders) {}方法棄用了,也就是我註釋掉的那個方法,使用 public static Sort by(List<Sort.Order> orders) {}代替:

public static Sort by(List<Sort.Order> orders) {
    Assert.notNull(orders, "Orders must not be null!");
    return orders.isEmpty() ? unsorted() : new Sort(orders);
}

關於使用原生SQL和JPQL的區別,下面舉個栗子來講明:

問題實現:根據姓名來查詢客戶;

SQL的實現:


    @QueryHints(value = {@QueryHint(name = HINT_COMMENT, value = "a query for pageable")})
    @Query(nativeQuery = true,value = "select * from customer c where c.first_name=:name or c.last_name=:name")
     Page<Customer> findByName(@Param("name") String name3, Pageable pageable);
 

  

JPQL的實現:


    @QueryHints(value = {@QueryHint(name = HINT_COMMENT, value = "a query for pageable")})
    @Query("select c from Customer c where c.firstName=:name or c.lastName=:name")
     Page<Customer> findByName(@Param("name") String name3, Pageable pageable);

總結

總結:原生的SQL直接使用數據庫表名和數據庫屬性名,JPQL會將數據庫映射成實體,使用JPQL更直觀我以爲!!

今天的學習就到這裏,還會有下一篇關於Spring Data JPA 深刻學習篇(二),但願一比一步從入門到掌握再到精通,一塊兒期待吧!!!

相關文章
相關標籤/搜索