spring data jpa Specification 复杂查询+分页查询
当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 复杂查询+分页查询的更多相关文章
- SpringBoot中使用Spring Data Jpa 实现简单的动态查询的两种方法
软件152 尹以操 首先谢谢大佬的简书文章:http://www.jianshu.com/p/45ad65690e33# 这篇文章中讲的是spring中使用spring data jpa,使用了xml ...
- spring data jpa 使用方法命名规则查询
按照Spring Data JPA 定义的规则,查询方法以findBy开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写.框架在进行方法名解析时,会先把方法名多余的前缀 ...
- 【hql】spring data jpa中 @Query使用hql查询 问题
spring data jpa中 @Query使用hql查询 问题 使用hql查询, 1.from后面跟的是实体类 不是数据表名 2.字段应该用实体类中的字段 而不是数据表中的属性 实体如下 hql使 ...
- Spring Data JPA 复杂/多条件组合查询
1: 编写DAO类或接口 dao类/接口 需继承 public interface JpaSpecificationExecutor<T> 接口: 如果需要分页,还可继承 public ...
- Spring Data JPA 实现多表关联查询
本文地址:https://liuyanzhao.com/6978.html 最近抽出时间来做博客,数据库操作使用的是 JPA,相对比 Mybatis 而言,JPA 单表操作非常方便,增删改查都已经写好 ...
- spring data jpa 使用JPQL的方式查询
用Spring Data JPA提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造查询条件,这时就可以使用@Query注解,结合JPQL的语句方式完成查询 @Que ...
- spring data jpa Specification动态查询
package com.ytkj.entity; import javax.persistence.*; import java.io.Serializable; /** * @Entity * 作用 ...
- 【spring data jpa】带有条件的查询后分页和不带条件查询后分页实现
一.不带有动态条件的查询 分页的实现 实例代码: controller:返回的是Page<>对象 @Controller @RequestMapping(value = "/eg ...
- Spring data JPA先排序再分页。
//工具类,增删改查等等package com.yunqing.service.impl; import java.util.Map; import org.springframework.beans ...
随机推荐
- yarn与npm对比
https://www.jianshu.com/p/254794d5e741(copy)
- ICPC — International Collegiate Programming Contest Asia Regional Contest, Yokohama, 2018–12–09 题解
目录 注意!!此题解存在大量假算法,请各位巨佬明辨! Problem A Digits Are Not Just Characters 题面 题意 思路 代码 Problem B Arithmetic ...
- 【BZOJ3931】[CQOI2015]网络吞吐量
Description 路由是指通过计算机网络把信息从源地址传输到目的地址的活动,也是计算机网络设计中的重点和难点.网络中实现路由转发的硬件设备称为路由器.为了使数据包最快的到达目的地,路由器需要选择 ...
- centos7 修改ali yum源
centos7 修改yum源为阿里源,某下网络下速度比较快 首先是到yum源设置文件夹里 安装base reop源 cd /etc/yum.repos.d 接着备份旧的配置文件 sudo mv Cen ...
- node、npm、git版本升级
node版本升级: npm install -g n 或者 npm i -g n --force n stable或者n --stable:安装最近稳定版本 n latest或者n --latest: ...
- Mac OS下Flutter环境搭建记录,VS Code开发
安装Flutter 获取FlutterSDK 终端cd进入SDK安装目录,比如 cd ~/FlutterSDK 由于在国内访问Flutter有时可能会受到限制,Flutter官方为中国开发者搭建了临时 ...
- Python基本语法_基本数据类型_序列类型详解
目录 目录 序列 序列的标准操作符 切片操作符 一个例子 字符串的连接 序列的功能函数 enumerate 枚举出序列对象的元素 len 获取序列对象的长度 min 取出sequence中的最小值 m ...
- Jedis源码浅析
1.概述 Jedis是redis官网推荐的redis java client,代码维护在github https://github.com/xetorthio/jedis. 本质上Jedis帮我们封装 ...
- Unity3D-Canvas-UIScaleMode
UnityCanvasUI自适应部分 用Canvas下的自适应UI Scale Mode: Constant Pixel Size:固定像素大小,无论如何UI怎么样就怎么样,只能通过 Scale Fa ...
- shell脚本批量开启防火墙端口
# 注意:shell脚本批量执行命令,不能只写一个函数,然后把所有命令复制进去,之前试过这样是不行的.必须要有一个判断命令执行成功与否的语句 # 简单的命令可以不加结果判断符号,但是遇到解压包.sed ...