springboot帶分頁的條件查詢


QueryDSL簡介

  QueryDSL僅僅是一個通用的查詢框架,專注於通過Java API構建類型安全的SQL查詢。
  Querydsl可以通過一組通用的查詢API為用戶構建出適合不同類型ORM框架或者是SQL的查詢語句,也就是說QueryDSL是基於各種ORM框架以及SQL之上的一個通用的查詢框架。
借助QueryDSL可以在任何支持的ORM框架或者SQL平台上以一種通用的API方式來構建查詢。目前QueryDSL支持的平台包括JPA,JDO,SQL,Java Collections,RDF,Lucene,Hibernate Search。
官網地址:http://www.querydsl.com/static/querydsl/4.1.3/reference/html_single/

 

  Querydsl 是一個類型安全的 Java 查詢框架,支持 JPA, JDO, JDBC, Lucene, Hibernate Search 等標准。類型安全( Type safety )和一致性( Consistency )是它設計的兩大准則。在 Spring Boot 中可以很好的彌補 JPA 的不靈活,實現更強大的邏輯。

1.配置到項目

第一步:Maven引入依賴:
  首先對於queryDSL有兩個版本,com.mysema.querydsl和com.querydsl,前者是3.X系列后者是4.X系列,這里使用的是后者.

 1 <dependency>
 2     <groupId>com.querydsl</groupId>
 3     <artifactId>querydsl-apt</artifactId>
 4     <scope>provided</scope>
 5 </dependency>
 6 
 7 <dependency>
 8     <groupId>com.querydsl</groupId>
 9     <artifactId>querydsl-jpa</artifactId>
10 </dependency>

第二步:加入插件,用於生成查詢實例,因為是類型安全的,所以還需要加上 Maven APT plugin ,使用 APT 自動生成一些類:

 1 <project>
 2   <build>
 3   <plugins>
 4     ...
 5     <plugin>
 6     <groupId>com.mysema.maven</groupId>
 7     <artifactId>apt-maven-plugin</artifactId>
 8     <version>1.1.3</version>
 9     <executions>
10         <execution>
11             <phase>generate-sources</phase>
12             <goals>
13                 <goal>process</goal>
14             </goals>
15             <configuration>
16                 <outputDirectory>target/generated-sources</outputDirectory>
17                 <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
18             </configuration>
19         </execution>
20     </executions>
21 </plugin>
22     ...
23   </plugins>
24   </build>
25 </project>

  執行mvn compile之后,可以找到該target/generated-sources/java,然后IDEA標示為源代碼目錄即可

3.基本概念

  每一個 Model (使用  @javax.persistence.Entity  注解的), Querydsl 都會在同一個包下生成一個以 Q 開頭(默認,可配置)的類,來實現便利的查詢操作。
 Spring 提供了一個便捷的方式使用 Querydsl ,只需要在 Repository 中繼承  QueryDslPredicateExecutor  即可:

 @Repository public interface NoticeRespository extends JpaRepository<Notice,Long>, QueryDslPredicateExecutor<Notice> { } 

然后就可以使用 UserRepository 無縫和 Querydsl 連接, QueryDslPredicateExecutor 接口提供了如下方法:

 1 public interface QueryDslPredicateExecutor<T> {  
 2   
 3     T findOne(Predicate predicate);  
 4   
 5     Iterable<T> findAll(Predicate predicate);  
 6   
 7     Iterable<T> findAll(Predicate predicate, Sort sort);  
 8   
 9     Iterable<T> findAll(Predicate predicate, OrderSpecifier<?>... orders);  
10   
11     Iterable<T> findAll(OrderSpecifier<?>... orders);  
12   
13     Page<T> findAll(Predicate predicate, Pageable pageable);  
14   
15     long count(Predicate predicate);  
16   
17     boolean exists(Predicate predicate);  
18 }  

4.Spring 參數支持解析 com.querydsl.core.types.Predicate

  根據用戶請求的參數自動生成 Predicate,這樣搜索方法不用自己寫啦,比如

1 @GetMapping("posts")
2 public Object posts(@QuerydslPredicate(root = Post.class) Predicate predicate) {
3     return postRepository.findAll(predicate);
4 }
5 // 或者順便加上分頁
6 @GetMapping("posts")
7 public Object posts(@QuerydslPredicate(root = Post.class) Predicate predicate, Pageable pageable) {
8     return postRepository.findAll(predicate, pageable);
9 }

  

然后請求:

/posts?title=title01 // 返回文章 title 為 title01 的文章 /posts?id=2 // 返回文章 id 為 2 的文章 /posts?category.name=Python // 返回分類為 Python 的文章(你沒看錯,可以嵌套,訪問關系表中父類屬性) /posts?user.id=2&category.name=Java // 返回用戶 ID 為 2 且分類為 Java 的文章

   Pageable  是Spring Data庫中定義的一個接口,該接口是所有分頁相關信息的一個抽象,通過該接口,我們可以得到和分頁相關所有信息(例如 pageNumber 、 pageSize 等)。
  Pageable定義了很多方法,但其核心的信息只有兩個:一是分頁的信息( page 、 size ),二是排序的信息。
  在springmvc的請求中只需要在方法的參數中直接定義一個 pageable 類型的參數,當Spring發現這個參數時,Spring會自動的根據request的參數來組裝該pageable對象,Spring支持的request參數如下:
 page ,第幾頁,從0開始,默認為第0頁
 size ,每一頁的大小,默認為20
 sort ,排序相關的信息,以property,property(,ASC|DESC)的方式組織,例如sort=firstname&sort=lastname,desc表示在按firstname正序排列基礎上按lastname倒序排列。

  這樣,我們就可以通過url的參數來進行多樣化、個性化的查詢。
Spring data提供了 @PageableDefault 幫助我們個性化的設置pageable的默認配置。例如 @PageableDefault(value = 15, sort = { "id" }, direction = Sort.Direction.DESC) 表示默認情況下我們按照id倒序排列,每一頁的大小為15。

1 @ResponseBody  
2 @RequestMapping(value = "list", method=RequestMethod.GET)  
3 public Page<blog> listByPageable(@PageableDefault(value = 15, sort = { "id" }, direction = Sort.Direction.DESC)   
4     Pageable pageable) {  
5     return blogRepository.findAll(pageable);  
6 }  

4.注意,這樣不會產生 SQL 注入問題的,因為不存在的屬性寫了是不起效果的, Spring 已經進行了判斷。

   再補充一點,你還可以修改默認行為,繼承 QueryDslPredicateExecutor 接口:

 1 @Repository
 2 public interface NoticeRespository extends JpaRepository<Notice,Long>, QueryDslPredicateExecutor<Notice>,QuerydslBinderCustomizer<QNotice> {
 3 
 4     default void customize(QuerydslBindings bindings,QNotice notice){
 5         bindings.bind(notice.content).first((path,value) ->path.contains(value).or(notice.title.contains(value)));
 6         bindings.bind(notice.firsttime).first((path,value) ->notice.pushdate.after(value));
 7         bindings.bind(notice.secondtime).first((path,value) ->notice.pushdate.before(value));
 8     }
 9 
10 }

  這樣你再訪問  /list?content=keywords  時,返回的是文章標題包含 keywords 或者內容包含keywords,而不是僅僅等於的所有文章啦! 

  而且訪問 /list?firsttime=2018.1.10&secontime=2018.1.11 時,返回的是pushdate在firsttime和secondtime之間的文章,只輸入firsttime是之后的所有,只輸入secondtime則是之前的所有文章。這樣大大簡化了操作!

5.實例

 

 1 package com.xunao.rubber.web.admin.notice;
 2 
 3 import com.xunao.rubber.domain.*;
 4 import com.xunao.rubber.repository.NoticeRespository;
 5 import org.springframework.beans.factory.annotation.Autowired;
 6 import org.springframework.data.domain.*;
 7 import org.springframework.data.querydsl.binding.QuerydslPredicate;
 8 import org.springframework.data.web.PageableDefault;
 9 import org.springframework.stereotype.Controller;
10 import org.springframework.ui.Model;
11 import org.springframework.web.bind.annotation.*;
12 import org.springframework.data.domain.Pageable;
13 import com.querydsl.core.types.Predicate;
14 
15 /**
16  * @author jinzhe
17  * @date 2018/1/10
18  */
19 @Controller
20 @RequestMapping("/admin/notice")
21 public class AdminNoticeRecordController {
22 
23     @Autowired
24     private NoticeRespository noticeRespository;
25 
26     @GetMapping("/list")
27     public String notice(Model model,Notice notice,
28                          @PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable,
29                          @QuerydslPredicate(root = Notice.class) Predicate predicate) {
30         Page<Notice> notices = noticeRespository.findAll(predicate, pageable);
31         model.addAttribute("notices", notices);
32         return "admin/notice/list";
33     }
34 }

 

 

 

   predicate 除了之前在Respository里寫的默認的條件外,還可以自己添加新的條件,例如:

predicate = QNotice.notice.pushdate.between(firsttime, secondtime).and(predicate); 
predicate = QNotice.notice.content.contains(keywords).and(predicate);

 


免責聲明!

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



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