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.基本概念
@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
这样,我们就可以通过url的参数来进行多样化、个性化的查询。
Spring data提供了 @PageableDefault 帮助我们个性化的设置pageable的默认配置。例如 @PageableDefault(value = 15, sort = { "id" }, direction = Sort.Direction.DESC)
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 已经进行了判断。
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
/list?firsttime=2018.1.10&secontime=2018.1.11
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
predicate = QNotice.notice.pushdate.between(firsttime, secondtime).and(predicate);
predicate = QNotice.notice.content.contains(keywords).and(predicate);