SpringData JPA條件查詢、排序、分頁查詢


前言

在剛開始學習的時候,在dao的定義的接口需要繼承JpaRepository<T, ID>接口和JpaSpecificationExecutor< T >接口,但是一直以來我用到的都只是JpaRepository,用於自動生成相關SQL語句簡化代碼。而JpaSpecificationExecutor給我的感覺就可有可無了,直到最近才發現它的用處,在此記錄一波。因為是學習筆記的關系,所以里面都只是截取關鍵的代碼進行記錄。

正文

要想使用Spring Data JPA,需要在pom.xml中添加以下依賴:

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependencies> 

 

如前言所說,帶條件的分頁查詢方法是被定義在JpaSpecificationExecutor接口中的,所以這里需要繼承這個接口。

/** * @author Veggie * @date 2019/8/14 - 14:11 */ @Repository public interface MessageRepository extends JpaRepository<Message, Long>, JpaSpecificationExecutor<Message> { } 

 

1. 條件查詢

自定義查詢條件的步驟:

  1. 實現Specification< T >接口(提供泛型;查詢的對象類型)
  2. 實現toPredicate方法(構造查詢條件)
  3. 需要借助方法參數中的兩個參數:
    root:獲取需要查詢的對象屬性
    CriteriaBuilder:構造查詢條件,內部封裝了很多查詢條件(模糊匹配,精准匹配)
//自定義查詢條件 Specification<Message> spec = new Specification<Message>() { @Override public Predicate toPredicate(Root<Message> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { //根據屬性名獲取查詢對象的屬性 Path<Message> path = root.get("receiverName"); //相當於 where receiverName = "Veggie", CriteriaBuilder接口中還有很多查詢條件,建議看源碼 Predicate equal = criteriaBuilder.equal(path, "Veggie"); return equal; } } //進行條件查詢,findAll()方法中的參數即為條件 List<Message> result = MessageRepository.findAll(spec); 

 

當查詢條件用到gt, ge, lt, le, like(分別表示>, >=, <, <=,模糊查詢)時,需要表明查詢對象屬性的類別,如下所示:

//查詢用戶名以"V"開頭的用戶 Predicate like = criteriaBuilder.like(path.as(String.class), "V%"); 

 

2. 排序

排序用到的是一個Sort類,它是查詢的排序選項(看源碼,有介紹)
它初始化是用到的參數第一個參數:
Sort.Direction.DESC表示降序
Sort.Direction.ASC表示升序

隨后的參數就是要排序的屬性列表,可以有多個參數,也可以直接用List傳,但是至少傳入一個屬性。

Sort sort = new Sort(Sort.Direction.DESC, "id"); //sort作為findAll()方法中的參數,查詢得到得到的結果是經過排序的 List<Message> result = MessageRepository.findAll(sort);

 

3.分頁查詢

分頁需要設置分頁參數類Pageable,初始化主要是有兩個參數:第一個是查詢的頁碼(下標從0開始),第二個是每頁查詢的條數,比如說結果有55條,如果每頁查詢10條,結果就會被分成6頁。也可以添加
注意:原來用到的new PageRequest()已經過時,現在用PageRequest.of()來實現。

Page接口是封裝為Spring Data Jpa 內部的page bean,它的常用方法如下:

//獲取總頁數 int getTotalPages(); //獲取總記錄數 long getTotalElements(); //獲取列表數據 List<T> getContent(); 

 

分頁查詢的代碼如下:

//設置分頁參數 Pageable pageable = PageRequest.of(0,5); //分頁查詢 Page<Message> page = MessageRepository.findAll(pageable); 
  • 1
  • 2
  • 3
  • 4

4. 完整的方法代碼

完整的代碼是以上三個知識點的集合,是帶條件的分頁查詢,查詢得到的結果按id號降序排序。

    @RequestMapping(path = "/page") public List<Message> queryPage(@RequestBody Map<String, Object> params) { /** * 自定義查詢條件 * 1. 實現Specification接口(提供泛型;查詢的對象類型) * 2. 實現toPredicate方法(構造查詢條件) * 3. 需要借助方法參數中的兩個參數( * root:獲取需要查詢的對象屬性 * CriteriaBuilder:構造查詢條件,內部封裝了很多查詢條件(模糊匹配,精准匹配) */ Specification<Message> spec = new Specification<Message>() { @Override public Predicate toPredicate(Root<Message> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { Path<Message> path = root.get("receiverName"); Predicate equal = criteriaBuilder.equal(path, "Veggie"); return equal; } }; Integer pageNo = Integer.valueOf((String) params.get("pageNo")); Integer pageSize = Integer.valueOf((String)params.get("pageSize")); /** * 添加排序Sort * Sort.Direction.DESC表示降序 * Sort.Direction.ASC表示升序 * properties是指實體類的屬性名(不是字段名) */ Sort sort = new Sort(Sort.Direction.ASC, "id"); /** * 分頁參數Pageable * 參數1:查詢的頁碼 * 參數2:每頁查詢的條數 * 參數3:查詢結果的排序規則(可選 */ Pageable pageable = PageRequest.of(pageNo, pageSize, sort); //原來的new PageRequest()已經過時 /** * 分頁查詢 * 參數1:查詢條件Specification * 參數2:分頁參數Pageable */ Page<Message> page = MessageRepository.findAll(spec, pageable); return page.getContent(); } 

 

感悟

既然都寫博客了,順帶記錄最近學習以及和大佬交談的感悟:

  1. 理解概念:在學習的時候,不能僅僅想着敲代碼,要多關注一些相關概念的理解,就比如說JPA、 Hibernate和SpringData JPA各自的基本概念,他們之間有什么關系之類的。
  2. 多看源碼:我具體也說不上來,就本能的感覺這個很重要。有時候一看源碼,可以理解之前想了很久都沒有想明白的問題。其次,看某個類或接口的源碼時,還可以了解到這個類可以提供什么方法(一般都會有英文注釋的)。
  3. 思維導圖:就像看書一定要想看目錄,學習新的內容應該先做個思維導圖。它以讓我們對一樣東西的總體有比較直觀、系統的認識,可以讓學習的思路變得清晰,可以避免浪費一些不必要 的時間。
  4. 多做筆記:連dalao都記不住之前學過的東西,何況我等凡夫俗子。做筆記不僅可以備忘,避免踩同樣的坑,還可以加深對該部分知識的理解。

情有獨鍾的JPA

平時在寫一些小項目時,比較喜歡引用 Spring Data Jpa,其實還是圖他寫代碼快~
在日常的開發工作中,分頁列表查詢基本是隨處可見,下面一起看一下如何使用 jpa 進行多條件查詢以及查詢列表分頁呢?

關於JPA的使用

關於 jpa 的使用,下面2步簡單過一下,詳細資料,小伙伴自行搜索一下吧~

1、導入依賴
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    mysql、web、druid......
</dependency>
2、配置yml

圖方便直接貼代碼了:

spring:
  # 數據源
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/tmax?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 1234
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.jdbc.Driver
  jpa:
    # 操作數據庫時顯示sql語句
    show-sql: true
    # 自動生成表結構
    generate-ddl: true
    hibernate:
      ddl-auto: none
    database-platform: org.hibernate.dialect.MySQL57Dialect

分頁查詢

我們了解 jpa 基本是不用去寫 sql 的,繼承 JpaRepository 即可,同樣也提供給了我們分頁查詢的方法,「補充:使用分頁需要同時繼承JpaSpecificationExecutor

舉例:

Page<VideoCategory> findByCondition(SearchVo searchVo, Pageable pageable);

通過傳入一個遵循 pageale 協議的對象來獲取某一頁的數據,通過源碼查看,發現 Pageable 是一個接口,提供了分頁一組方法的聲明,如第幾頁,每頁多少條記錄,排序信息等,部分方法如下:

int getPageNumber();

int getPageSize();

int getOffset();

Sort getSort();

Pageable next();

Pageable previousOrFirst();

Pageable first();

boolean hasPrevious();

通過這些方法我們可以構造我們的 pageable 對象,需要注意的是 jpa 在構造頁碼初始時,是從 0 開始的。

廢話不多說,來看一段代碼吧:

1. impl
    @Override
    public Page<VideoCategory> findByCondition(VideoCategory videoCategory, SearchVo searchVo, Pageable pageable) {

        return videoCategoryDao.findAll(new Specification<VideoCategory>() {
            @Nullable
            @Override
            public Predicate toPredicate(Root<VideoCategory> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {

                /** 可添加你的其他搜索過濾條件 默認已有創建時間過濾 **/
                Path<Date> createTimeField=root.get("createTime");
                Path<String> categoryIdField=root.get("categoryId");

                List<Predicate> list = new ArrayList<Predicate>();

                /** 創建時間 **/
                if(StrUtil.isNotBlank(searchVo.getStartDate())&&StrUtil.isNotBlank(searchVo.getEndDate())){
                    Date start = DateUtil.parse(searchVo.getStartDate());
                    Date end = DateUtil.parse(searchVo.getEndDate());
                    list.add(cb.between(createTimeField, start, DateUtil.endOfDay(end)));
                }

                /** 視頻分類 **/
                if(StrUtil.isNotBlank(videoCategory.getCategoryId())){        
                    list.add(cb.equal(categoryIdField,videoCategory.getCategoryId()));
                }

                Predicate[] arr = new Predicate[list.size()];
                cq.where(list.toArray(arr));
                return null;
            }
        }, pageable);
    }
2. controller
    @RequestMapping(value = "/getByCondition", method = RequestMethod.GET)
    @ApiOperation(value = "多條件分頁獲取")
    public Result<Page<VideoCategory>> getByCondition(
            @ModelAttribute VideoCategory videoCategory,
            @ModelAttribute SearchVo searchVo,
            @ModelAttribute PageVo pageVo){

        Page<VideoCategory> page = videoCategoryService.findByCondition(videoCategory, searchVo, PageUtil.initPage(pageVo));
        return new ResultUtil<Page<VideoCategory>>().setData(page);
    }
3. PageUtil
public static Pageable initPage(PageVo page){

        Pageable pageable = null;
        int pageNumber = page.getPageNumber();
        int pageSize = page.getPageSize();
        String sort = page.getSort();
        String order = page.getOrder();

        if(pageNumber<1){
            pageNumber = 1;
        }
        if(pageSize<1){
            pageSize = 10;
        }
        if(StrUtil.isNotBlank(sort)) {
            Sort.Direction d;
            if(StrUtil.isBlank(order)) {
                d = Sort.Direction.DESC;
            } else {
                d = Sort.Direction.valueOf(order.toUpperCase());
            }
            Sort s = new Sort(d, sort);
            pageable = PageRequest.of(pageNumber-1, pageSize, s);
        } else {
            pageable = PageRequest.of(pageNumber-1, pageSize);
        }
        return pageable;
    }



link: https://www.cnblogs.com/niceyoo/p/10817290.html

 Page<WorkWeight> page = workWeightRepository.findAll(new Specification<WorkWeight>() {
            @Override
            public Predicate toPredicate(Root<WorkWeight> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> list = new ArrayList<>();
 
                if(null != subDepartmentId && !"".equals("subDepartmentId")){
                    list.add(cb.equal(root.<String>get("subDepartmentId"), subDepartmentId));
                }
                if(null != status && !"".equals("status")){
                    list.add(cb.equal(root.<String>get("status"), status));
                }
                if (null != startTime && !"".equals("startTime")) {
                    list.add(cb.greaterThanOrEqualTo(root.<Date>get("startTime"), DateUtil.stringToDate(startTime)));
                }
                if (null != endTime && !"".equals("endTime")) {
                    list.add(cb.lessThanOrEqualTo(root.<Date>get("endTime"), DateUtil.stringToDate(endTime)));
                }
                Expression<String> exp = root.<String>get("departmentId");
                list.add(exp.in(idList)); // 往in中添加所有id 實現in 查詢
 
                if (list.size() != 0) {
                    Predicate[] p = new Predicate[list.size()];
                    return cb.and(list.toArray(p));
                } else {
                    return null;
                }
            }
        }, new PageRequest(pageable.getPageNumber(), pageable.getPageSize(), pageable.getSort()));
@Override
    public Page<泛型> findRecordList(int couponDetailId, int pageNum, int pageSize, String startTime, String endTime) {
        try {
        //排序規則和分頁
            Sort sort = new Sort(new Sort.Order(Sort.Direction.DESC, "createTime"));
            PageRequest pageRequest = new PageRequest(pageNum - 1, pageSize, sort);
            Specification specification = new Specification() {
                @Override
                public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
            //增加篩選條件
                    Predicate predicate = cb.conjunction();
                    predicate.getExpressions().add(cb.equal(root.get("cardId"), couponDetailId));
            //起始日期
                    if (startTime != null && !startTime.trim().equals("")) {
                        predicate.getExpressions().add(cb.greaterThanOrEqualTo(root.get("createTime").as(String.class), startTime));
                    }
            //結束日期
                    if (endTime != null && !endTime.trim().equals("")) {
                        predicate.getExpressions().add(cb.lessThanOrEqualTo(root.get("createTime").as(String.class), endTime));
                    }
                    return predicate;
                }
            };
            Page all = discountCouponRecordDao.findAll(specification, pageRequest);
  return all;
}

  

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM