当Repository接口继承了JpaSpecificationExecutor后,我们就可以使用如下接口进行分页查询:

    /**
* Returns a {@link Page} of entities matching the given {@link Specification}.
*
* @param spec can be {@literal null}.
* @param pageable must not be {@literal null}.
* @return never {@literal null}.
*/
Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);

结合jpa-spec可以很容易构造出Specification:

jpa-spec github地址:https://github.com/wenhao/jpa-spec

public Page<Person> findAll(SearchRequest request) {
Specification<Person> specification = Specifications.<Person>and()
.eq(StringUtils.isNotBlank(request.getName()), "name", request.getName())
.gt(Objects.nonNull(request.getAge()), "age", 18)
.between("birthday", new Date(), new Date())
.like("nickName", "%og%", "%me")
.build(); return personRepository.findAll(specification, new PageRequest(0, 15));
}

单表查询确实很简单,但对复杂查询,就复杂上些了:

public List<Phone> findAll(SearchRequest request) {
Specification<Phone> specification = Specifications.<Phone>and()
.eq(StringUtils.isNotBlank(request.getBrand()), "brand", "HuaWei")
.eq(StringUtils.isNotBlank(request.getPersonName()), "person.name", "Jack")
.build(); return phoneRepository.findAll(specification);
}

这里主表是phone,使用了person的name做条件,使用方法是person.name。

jpa-spec内部会分析person.name,如下代码:

public From getRoot(String property, Root<T> root) {
if (property.contains(".")) {
String joinProperty = StringUtils.split(property, ".")[0];
return root.join(joinProperty, JoinType.LEFT);
} else {
return root;
}
}

就可看到它用了root.join,那就有一个问题,如果有两个person字段的条件,那就要再join一次,就会生成这样的sql:

select * from phone left outer join person on XX=XX left outer join person XX=XX.

这样肯定不满足需求。这应该也是jpa-spec的一个bug吧

为了解决这个问题,可以使用它提供的另一种方式查询:

public List<Phone> findAll(SearchRequest request) {
Specification<Person> specification = Specifications.<Person>and()
.between("age", 10, 35)
.predicate(StringUtils.isNotBlank(jack.getName()), ((root, query, cb) -> {
Join address = root.join("addresses", JoinType.LEFT);
return cb.equal(address.get("street"), "Chengdu");
}))
.build(); return phoneRepository.findAll(specification);
}

这要就可以解决大多数情况了,除了分页

看下正常的单表分页+排序查询:

public Page<Person> findAll(SearchRequest request) {
Specification<Person> specification = Specifications.<Person>and()
.eq(StringUtils.isNotBlank(request.getName()), "name", request.getName())
.gt("age", 18)
.between("birthday", new Date(), new Date())
.like("nickName", "%og%")
.build(); Sort sort = Sorts.builder()
.desc(StringUtils.isNotBlank(request.getName()), "name")
.asc("birthday")
.build(); return personRepository.findAll(specification, new PageRequest(0, 15, sort));
}

如果在此基础上增加关联,如下代码:

public Page<Person> findAll(SearchRequest request) {
Specification<Person> specification = Specifications.<Person>and()
.predicate(StringUtils.isNotBlank(jack.getName()), ((root, query, cb) -> {
Join address = root.join("addresses", JoinType.LEFT);
return cb.equal(address.get("street"), "Chengdu");
}))
.eq(StringUtils.isNotBlank(request.getName()), "name", request.getName())
.gt("age", 18)
.between("birthday", new Date(), new Date())
.like("nickName", "%og%")
.build(); Sort sort = Sorts.builder()
.desc(StringUtils.isNotBlank(request.getName()), "name")
.asc("birthday")
.build(); return personRepository.findAll(specification, new PageRequest(0, 15, sort));
}

就会发现addresses的延迟加载失效,生成很多查询addresses的语句,解决方案如下:

public Page<Person> findAll(SearchRequest request) {
Specification<Person> specification = Specifications.<Person>and()
.predicate(StringUtils.isNotBlank(jack.getName()), ((root, query, cb) -> {
Join address;
if (Long.class != query.getResultType()) {
address = (Join) root.fetch("addresses", JoinType.LEFT);
} else {
address = root.join("addresses", JoinType.LEFT);
}
return cb.equal(address.get("street"), "Chengdu");
}))
.eq(StringUtils.isNotBlank(request.getName()), "name", request.getName())
.gt("age", 18)
.between("birthday", new Date(), new Date())
.like("nickName", "%og%")
.build(); Sort sort = Sorts.builder()
.desc(StringUtils.isNotBlank(request.getName()), "name")
.asc("birthday")
.build(); return personRepository.findAll(specification, new PageRequest(0, 15, sort));
}

至此,用Specification查询就应该够用了,再配合JpaRepository (SimpleJpaRepository)提供的方法 和@Query注解方法,和criteria api查询,这四种JPA查询就可以解决大多数应用问题了。

spring data jpa Specification 复杂查询+分页查询的更多相关文章

  1. SpringBoot中使用Spring Data Jpa 实现简单的动态查询的两种方法

    软件152 尹以操 首先谢谢大佬的简书文章:http://www.jianshu.com/p/45ad65690e33# 这篇文章中讲的是spring中使用spring data jpa,使用了xml ...

  2. spring data jpa 使用方法命名规则查询

    按照Spring Data JPA 定义的规则,查询方法以findBy开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写.框架在进行方法名解析时,会先把方法名多余的前缀 ...

  3. 【hql】spring data jpa中 @Query使用hql查询 问题

    spring data jpa中 @Query使用hql查询 问题 使用hql查询, 1.from后面跟的是实体类 不是数据表名 2.字段应该用实体类中的字段 而不是数据表中的属性 实体如下 hql使 ...

  4. Spring Data JPA 复杂/多条件组合查询

    1: 编写DAO类或接口  dao类/接口 需继承 public interface JpaSpecificationExecutor<T> 接口: 如果需要分页,还可继承 public ...

  5. Spring Data JPA 实现多表关联查询

    本文地址:https://liuyanzhao.com/6978.html 最近抽出时间来做博客,数据库操作使用的是 JPA,相对比 Mybatis 而言,JPA 单表操作非常方便,增删改查都已经写好 ...

  6. spring data jpa 使用JPQL的方式查询

    用Spring Data JPA提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造查询条件,这时就可以使用@Query注解,结合JPQL的语句方式完成查询 @Que ...

  7. spring data jpa Specification动态查询

    package com.ytkj.entity; import javax.persistence.*; import java.io.Serializable; /** * @Entity * 作用 ...

  8. 【spring data jpa】带有条件的查询后分页和不带条件查询后分页实现

    一.不带有动态条件的查询 分页的实现 实例代码: controller:返回的是Page<>对象 @Controller @RequestMapping(value = "/eg ...

  9. Spring data JPA先排序再分页。

    //工具类,增删改查等等package com.yunqing.service.impl; import java.util.Map; import org.springframework.beans ...

随机推荐

  1. js中Array方法归类解析

    为什么要对Array方法进行归类解析 因为它常用,而且面试必问 改变原数组的方法 pop 删除并返回数组最后一个元素push 从末尾给数组添加元素,返回新数组length值reverse 颠倒数组元素 ...

  2. CSS注

    1.css3内容上下左右居中 .box { display:-moz-box; -moz-box-pack:center; -moz-box-align:center; display:-webkit ...

  3. (转)GitBlit安装

    转:https://blog.csdn.net/qq_32599479/article/details/90748371 GitBlit的安装本文是基于Windows 10系统环境,安装和测试GitB ...

  4. QPS、TPS、PV、UV、GMV、IP、RPS

    摘自:https://www.citrons.cn/jishu/226.html 关于 QPS.TPS.PV.UV.GMV.IP.RPS 这些词语,看起来好像挺专业.但实际上,我认为是这是每个程序员必 ...

  5. 【机器学习速成宝典】模型篇02线性回归【LR】(Python版)

    目录 什么是线性回归 最小二乘法 一元线性回归 多元线性回归 什么是规范化 Python代码(sklearn库) 什么是线性回归(Linear regression) 引例 假设某地区租房价格只与房屋 ...

  6. 打开远程桌面时总提示无法打开连接文件default.rdp

    删除C:\Users\Administrator\Documents\default.rdp,再启动远程就好了 http://www.chahushequ.com/read-topic-94-2fa9 ...

  7. 用ps 查看线程状态

    ps -eLo pid,tid,class,rtprio,ni,pri,psr,pcpu,pmem,stat,wchan:30,comm 线程相关选项: THREAD DISPLAY H Show t ...

  8. (转)使用NMAP工具扫描端口

    原文:http://www.linuxde.net/2013/02/12354.html nmap 是一个用于网络探索或安全评测的工具.它支持 ping 扫描(判定哪些主机在运行),多端口扫描技术(判 ...

  9. EncodeError: 'latin-1' codec can't encode characters in position 69-70: ordinal not in range(256)

    UnicodeEncodeError: 'latin-1' codec can't encode characters in position 69-70: ordinal not in range( ...

  10. Java中的内存泄露 和 JVM GC(垃圾回收机制)

    一.什么是Java中的内存泄露? 在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点, 首先,这些对象是可达的,即在有向图中,存在通路可以与其相连:其次,这些对象是无用的,即程序以 ...