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 ...
随机推荐
- html applet标签 语法
html applet标签 语法 作用:定义嵌入的 applet. 说明:某些浏览器中依然存在对 <applet> 但是需要额外的插件和安装过程才能起作用.大理石机械构件 注释:HTML5 ...
- 虚拟机使用桥接模式连接网络并且设置静态ip
1.桥接模式连接网络 虚拟机连接网络一共有四种模式,我这里只介绍桥接模式,毕竟坑了我几个小时 设置有线连接,我本来用的无线连接完成微信点餐系统,后来换了有线因为有线连接不会分配ip,和本地电脑使用同一 ...
- jQuery字体图标的三种方法
BootStrap框架原生图标 在导入BootStrap包的同时,导入bootstrap-3.3.7-dist/css/bootstrap.css层叠样式; <button type=" ...
- Codechef SEAARC Sereja and Arcs (分块、组合计数)
我现在真的什么都不会了呢...... 题目链接: https://www.codechef.com/problems/SEAARC 好吧,这题其实考察的是枚举的功力-- 题目要求的是\(ABAB\)的 ...
- nowcoder---常州大学新生寒假训练会试----F 大佬的生日礼包(二分)
链接:https://www.nowcoder.net/acm/contest/78/F 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32768K,其他语言65536K 64bit ...
- [LeetCode]-DataBase-Rising Temperature
Given a Weather table, write a SQL query to find all dates' Ids with higher temperature compared to ...
- Gridview中显示的值根据数据库中带出的值作更改
前台页面对Gridview增加事件 OnRowDataBound="GridView1_RowDataBound"protected void GridView1_RowDataB ...
- leetcode-mid- math-166. Fraction to Recurring Decimal
mycode 73.92% 如何判断同号? 1)res = "-" if ((numerator>0) ^ (denominator>0)) else " ...
- leetcode-mid-sorting and searching -347. Top K Frequent Elements
mycode 71.43% class Solution(object): def topKFrequent(self, nums, k): """ :type nu ...
- mysql 全量备份以及增量备份
MySQL 的全量备份很简单,增量备份虽然会手动使用但是还没写过脚本去实现增量备份.今天搞一搞,顺便回忆一下MySQL的基本操作.