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 ...
随机推荐
- sh_11_九九乘法表
sh_11_九九乘法表 # 1. 打印 9 行小星星 row = 1 while row <= 9: col = 1 while col <= row: # print("*&q ...
- linux文件夹目录含义及用途
/boot,存放linux启动文件和内核: /initrd,boot loader initialized RAM disk,就是由boot loader初始化的内存盘.在linux内核启动前,boo ...
- mini-batch
我们在训练神经网络模型时,最常用的就是梯度下降,梯度下降有一下几种方式: 1.Batch gradient descent(BGD批梯度下降) 遍历全部数据集算一次损失函数,然后算函数对各个参数的梯度 ...
- Visual Studio 2019 企业版 注册码 百度云下载
微软官网下载地址:https://visualstudio.microsoft.com/zh-hans/downloads/ Key: Visual Studio 2019 Enterprise 企业 ...
- 打开远程桌面时总提示无法打开连接文件default.rdp
删除C:\Users\Administrator\Documents\default.rdp,再启动远程就好了 http://www.chahushequ.com/read-topic-94-2fa9 ...
- 如果将get请求转换成post请求
td><a href="emp/${emp.id}">Edit</a></td> <form action="" ...
- 2、Shiro的认证
Shiro的认证流程大体可以参考下面这幅图: 但是没有接触过shiro的同学看到上面的图片也不明白,下面我们来在代码中尝试体验Shiro的认证过程: 1.新建一个SpringBoot项目项目结构如下: ...
- 1、Shiro简介以及整体架构
1.Shiro概念和作用: 利用shiro可以快速完成权限管理模块的开发 Spring的官网也是用Shiro做安全管理的... Shiro整体架构: 可能你感觉上面的图片很乱,但是你一定要先大体有个印 ...
- 用Vue来实现音乐播放器(二十一):歌手详情数据抓取
第一步:在api文件夹下的singer.js中抛出getSingerDetail方法 第二步:在singer-detail.vue组件中引入api文件夹下的singer.js和config.js 第三 ...
- TCP 首部格式
<图解TCP/IP> 6.7 TCP的首部格式 TCP中没有表示包长度和数据长度的字段.可由IP层获知TCP的包长由TCP的包长可知数据的长度. 源端口号:表示发送端端口号,字段长16位 ...