【前言说明】

针对CRUD种的查询,因为我们的查询总是具有各种各样的筛选条件

为了我们的程序能够更加适应筛选条件的变化,SpringDataJpa提供了Specifications这种解决方案

Specifications 本意表示规范

也就是说我们的筛选条件也将需要被规范化

按照SpringDataJpa设计好的方式执行即可

【接口说明】

所在包位置:

org.springframework.data.jpa.repository.JpaSpecificationExecutor;

接口名称:Java持久化接口规范处理器

所有抽象方法:

    Optional<T> findOne(@Nullable Specification<T> var1);

    List<T> findAll(@Nullable Specification<T> var1);

    Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);

    List<T> findAll(@Nullable Specification<T> var1, Sort var2);

    long count(@Nullable Specification<T> var1);

1、所有方法的Specification参数都被注解@Nullable,表示这个参数可以为Null,即表明可以无Specification条件来执行

2、findOne是表明查询单个记录,Specification即表明是一个筛选条件的对象

3、两种查询所有的findAll,其中一种必须要求排序参数,用于确定的排序需求使用

4、我们知道分页必须要两个SQL执行,所以这里就有了Page & Long ,写过分页功能的就一定知道是组合使用的

查看这个Specification,发现它也是一个接口

org.springframework.data.jpa.domain
public interface Specification<T> extends Serializable

在我们之前的学习中我们的筛选条件越来越多,我们不可能再为各个筛选条件编写对应的参数

所以那个时候我们就需要统一起来各种筛选条件需要的参数就全部归纳为一个类,这就是筛选条件的参数类

但是不同的实体映射类,即我们的表的需要完成的功能不一样,自然而然筛选的条件也不一样

固定的一个条件参数类依然无法满足更多ORM的需要,则进一步上升为一个条件参数规范

所以这就是Specification

而具体的条件细节则由我们自己来完成:

【虽然他已经设定好默认的一些东西了。。。】

    static <T> Specification<T> not(@Nullable Specification<T> spec) {
return spec == null ? (root, query, builder) -> {
return null;
} : (root, query, builder) -> {
return builder.not(spec.toPredicate(root, query, builder));
};
} @Nullable
static <T> Specification<T> where(@Nullable Specification<T> spec) {
return spec == null ? (root, query, builder) -> {
return null;
} : spec;
} @Nullable
default Specification<T> and(@Nullable Specification<T> other) {
return SpecificationComposition.composed(this, other, (builder, left, rhs) -> {
return builder.and(left, rhs);
});
} @Nullable
default Specification<T> or(@Nullable Specification<T> other) {
return SpecificationComposition.composed(this, other, (builder, left, rhs) -> {
return builder.or(left, rhs);
});
} @Nullable
Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3);

对toPredicate的介绍:

要求参数:

Root 查询的根对象(查询的任何属性从根对象获取)
CriteriaQuery 顶层查询对象,自定义查询方式
CriteriaQueryBuilder 查询构建器 封装了很多查询条件

单个记录单个条件的查询:

实现这个规范接口,并且重写条件方法

从root对象获取筛选条件的字段【我需要根据什么字段来执行筛选条件?】

通过该Path对象被条件构建器注入和比较值进行比较

返回这个规范结果给我们的Dao方法使用

    @Test /* 查询单个记录  单个查询条件 */
public void findOne() {
Specification<User> userSpecification = new Specification<User>(){ public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
// 获取比较的属性
Path<Object> user_id = root.get("user_id");
// 构建筛选条件 需要比较的字段,字段的值
// Predicate predicate = criteriaBuilder.equal(user_id, 2);
return criteriaBuilder.equal(user_id, 2); // 返回我们的比较结果?
}
}; Optional<User> optionalUser = userRepository.findOne(userSpecification);
User user = optionalUser.get();
System.out.println(user);

单个记录多个条件的查询:

使用构建器的and & or 来合并条件

    @Test /* 查询单个记录 查询多个条件 */
public void findOnes() { /* Lambda表达式 */
Specification<User> userSpecification = (Specification<User>) (root, criteriaQuery, criteriaBuilder) -> {
// 获取比较的属性
Path<Object> user_id = root.get("user_id");
Path<Object> user_name = root.get("user_name"); Predicate predicate01 = criteriaBuilder.equal(user_id, 2);
Predicate predicate02 = criteriaBuilder.equal(user_name, "user01"); // 如果多条件就合并条件
// criteriaBuilder.and(predicate01, predicate02)
// 或者不是全要求的条件,多条件其中一个满足的情况
// criteriaBuilder.or(predicate01, predicate02)
// 具体情况根据实际需求来抉择,或者组合 return criteriaBuilder.and(predicate01, predicate02);
}; Optional<User> optionalUser = userRepository.findOne(userSpecification);
User user = optionalUser.get();
System.out.println(user);
}

模糊条件的查询:

    @Test /* 查询多个记录 查询条件:模糊查询 */
public void findOneByLike() { /* Lambda表达式 */
Specification<User> userSpecification = (Specification<User>) (root, criteriaQuery, criteriaBuilder) -> {
// 获取比较的属性
Path<Object> user_name = root.get("user_name");
// 模糊要求指定参数类型
return criteriaBuilder.like(user_name.as(String.class), "%user%");
}; List<User> userList = userRepository.findAll(userSpecification);
for (User user : userList) {
System.out.println(user);
}
}

多记录排序条件查询:

    @Test /* 查询多个记录 查询条件:模糊查询, 排序查询 */
public void findOneByLikeAndSort() { /* Lambda表达式 */
Specification<User> userSpecification = (Specification<User>) (root, criteriaQuery, criteriaBuilder) -> {
// 获取比较的属性
Path<Object> user_name = root.get("userName");
// 模糊要求指定参数类型
return criteriaBuilder.like(user_name.as(String.class), "%use%");
}; // 参数?
// Sort sortOrders = new Sort(Sort.Direction.DESC, "user_name"); Sort sort = Sort.by(Sort.Direction.DESC, "userName"); List<User> userList = userRepository.findAll(userSpecification, sort); for (User user : userList) {
System.out.println(user);
}
}

排序条件查询注入的属性要求:

注意这里排序使用的对象属性!

因为这个错误导致我的实体类必须做出更改

package cn.echo42.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor; import javax.persistence.*; /**
* @author DaiZhiZhou
* @file Spring-Data-JPA
* @create 2020-07-31 22:56
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "sys_user")
public class User { @Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Integer userId; // @Column(name = "user_id")
@Column(name = "user_name")
private String userName; // @Column(name = "user_name")
@Column(name = "user_password")
private String userPassword; // @Column(name = "user_password")
@Column(name = "user_status")
private Integer userStatus; // @Column(name = "user_status")
@Column(name = "user_is_del")
private Integer userIsDel; // @Column(name = "user_is_del")
}

详细原因是因为,排序的String参数properties,会将下划线作为字段的分隔符

我的user_name,就被分成user,name。这样就无法匹配了啊。

解决方案是被迫更改实体类的属性字段为驼峰命名方式,并且注解上对应的表字段

参考地址:

https://zhidao.baidu.com/question/1823902339135786828.html

排序查询的Sort条件对象的分析:

视频参考地址:

https://www.bilibili.com/video/BV1WJ411j7TP?t=271&p=67

在老版本的SpringDataJPA中,原先的Sort允许被直接带参构造的NEW出来

Sort sortOrders = new Sort(Sort.Direction.DESC, "user_name");

但是在现在的新版本中不再允许:

Sort的构造器不再允许被外部访问

    private Sort(Sort.Direction direction, List<String> properties) {
if (properties != null && !properties.isEmpty()) {
this.orders = (List)properties.stream().map((it) -> {
return new Sort.Order(direction, it);
}).collect(Collectors.toList());
} else {
throw new IllegalArgumentException("You have to provide at least one property to sort by!");
}
} @Generated
protected Sort(List<Sort.Order> orders) {
this.orders = orders;
}

第一种方式要求Direction对象和一个Properties的集合,

Direction是一个枚举类,意思是顺序要求,无非就是ASC & DESC

Properties就是我们需要排序的字段,一个或者是多个的存在

但是现在这个构造器不可使用了。。。

第二种方式要求一个Order对象的集合,SpringDataJPA的设计者希望由Order对象来封装排序条件,

再装入Sort处理,每一个Order都是对应的字段和顺序的要求。

这也反映出来,第一种的弊端就是只能对所有的字段同时ASC或者DESC,并不能分开要求

但是Sort类提供了静态方法来实现对象的创建:

第一种提供properties属性即可,要求的字段均以默认的ASC排序

    public static Sort by(String... properties) {
Assert.notNull(properties, "Properties must not be null!");
return properties.length == 0 ? unsorted() : new Sort(DEFAULT_DIRECTION, Arrays.asList(properties));
}

第二种提供Orders对象集合,对各个字段的排序要求是独立的

    public static Sort by(List<Sort.Order> orders) {
Assert.notNull(orders, "Orders must not be null!");
return orders.isEmpty() ? unsorted() : new Sort(orders);
}

第三种就是由可变参数实现,和第二种区别不大

    public static Sort by(Sort.Order... orders) {
Assert.notNull(orders, "Orders must not be null!");
return new Sort(Arrays.asList(orders));
}

第四种就是指定顺序条件,第一种的补充

    public static Sort by(Sort.Direction direction, String... properties) {
Assert.notNull(direction, "Direction must not be null!");
Assert.notNull(properties, "Properties must not be null!");
Assert.isTrue(properties.length > 0, "At least one property must be given!");
return by((List)Arrays.stream(properties).map((it) -> {
return new Sort.Order(direction, it);
}).collect(Collectors.toList()));
}

多记录分页条件查询:

分页条件SpringDataJPA要求一个Pageable类型的对象传入

Pageable是一个接口,寓意可翻页的

实现类有一个PageRequest

打开PageRequest,同样的,不允许调用构造器创建对象

    protected PageRequest(int page, int size, Sort sort) {
super(page, size);
Assert.notNull(sort, "Sort must not be null!");
this.sort = sort;
}

但是它由和Sort一样提供了三种静态方法:

    public static PageRequest of(int page, int size) {
return of(page, size, Sort.unsorted());
} public static PageRequest of(int page, int size, Sort sort) {
return new PageRequest(page, size, sort);
} public static PageRequest of(int page, int size, Direction direction, String... properties) {
return of(page, size, Sort.by(direction, properties));
}

第一个仅仅要求当前页数和每页显示的记录数量

第二个多了一个排序对象要求,即我们分页之后再对这个结果集排序【盲猜】

第三个就是多字段统一顺序条件要求

对page条件的纠结:

注意这里的page参数,以往我们的SQL的LIMIT查询

是startPosition & sizeLimitation,即起始位置和记录长度限制

分页的页数需要转换成起始位置进行查询

也就是这个:

(当前页码 - 1)*  每页显示数量 = 起始位置

在SpringDataJPA这里已经帮我们处理好了,但是起始位置是从0开始

【需要处理一个再减一。。。】

演示案例:

    @Test /* 查询多个记录 查询条件:分页查询 */
public void findAllByPaging() { /* Lambda表达式 */
Specification<User> userSpecification = (Specification<User>) (root, criteriaQuery, criteriaBuilder) -> {
// 获取比较的属性
Path<Object> user_name = root.get("userName");
// 模糊要求指定参数类型
return criteriaBuilder.like(user_name.as(String.class), "%use%");
}; Pageable pageable = PageRequest.of(1,2); Page<User> userPage = userRepository.findAll(userSpecification, pageable);
List<User> userList = userPage.getContent(); for (User user : userList) {
System.out.println(user);
} // 获取总记录数量 long totalElements = userPage.getTotalElements();
// 获取总页数 int totalPages = userPage.getTotalPages();
}

Specifications动态查询的更多相关文章

  1. 22 Specifications动态查询

    Specifications动态查询 有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationE ...

  2. Spring Data JPA 的 Specifications动态查询

    主要的结构: 有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询. ...

  3. spring data jpa Specification动态查询

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

  4. Thinkphp查询 1.查询方式 2.表达式查询 3.快捷查询 4.区间查询 5.组合查询 6.统计查询 7.动态查询 8.SQL 查询

    1.使用字符串作为条件查询 $user = M('User'); var_dump($user->where('id=1 AND user="蜡笔小新"')->sele ...

  5. Linq 动态查询排序

    Linq的排序一般是这样写的: query.OrderBy(x => x.Tel).Skip().Take(); 实际使用中排序字段可能是通过字符类型的参数来设置的,于是想这样实现: query ...

  6. ibatis动态查询条件

    ibatis的调试相对困难,出错的时候主要依据是log4生成的log文件和出错提示,这方面要能比较熟练的看懂. 下面这个配置基本上包含了最复杂的功能:分页\搜索\排序\缓存\传值Hash表\返回has ...

  7. 自己写的一个关于Linq to Entity 动态查询的例子

    这两天一直想写一个动态查询的方式,先是晚上查询了一下,发现大家写的差不多都是一样的[如:http://www.cnblogs.com/ASPNET2008/archive/2012/10/28/274 ...

  8. Linq动态查询简易解决之道(原创)

    因为项目需要使用Linq来查询数据,但是在多条件查询时,需要使用一大堆if(...!=string.empty)等判断条件感觉不是很优雅.网上搜索以下,大概找到了两种办法,一种是老外写的一个类,感觉用 ...

  9. SSH动态查询封装接口介绍

    SSH动态查询封装接口介绍 1.查询记录总条数 public int count(Class c,Object[][] eq,Object[][] like,String[] group,String ...

  10. Linq to sql 实现多条件的动态查询(方法一)

    /// <summary> /// Linq to sql 多字段动态查询 /// </summary> /// <returns></returns> ...

随机推荐

  1. USB硬件特性(速度、名称、供电)

    USB传输速度 USB1.0版本,USB LS(Low Speed低速),速度1.5Mbps. USB1.1版本,USB FS(Full Speed全速),速度12Mbps. USB2.0版本,USB ...

  2. 强大的USB协议分析工具

    2020年最后一天了,感谢大家一年来对我文章的支持,有你们的支持就是我强大的动力. 今天来给大家介绍一个USB 协议分析软件LeCroy USB Advisor,软件安装包下载连接如下: 链接:htt ...

  3. 你对 Vue.js 的template 编译的理解?

    template 是 ES5 新出的语法 ,template 是不会被页面显示的,但是 vue 中会被翻译成 dom 结构 : template 编译的过程 : parse 解析生成ast 抽象语法树 ...

  4. 大模型存储选型 & JuiceFS 在关键环节性能详解

    从去年开始,LLM大语言模型领域发展迅速.如 LLaMA.ChatGLM.Baichuan.Qwen 和 yi-model 等基础模型(Foundation Models)的数量显著增加.众多企业也开 ...

  5. Leetcode Practice --- 栈和队列

    目录 155. 最小栈 思路解析 20. 有效的括号 思路解析 1047. 删除字符串中的所有相邻重复项 思路解析 1209. 删除字符串中的所有相邻重复项 II 思路解析 删除字符串中出现次数 &g ...

  6. Kubernetes 对接 GlusterFS 磁盘扩容实战

    前言 知识点 定级:入门级 使用 Heketi Topology 扩容磁盘 使用 Heketi CLI 扩容磁盘 实战服务器配置 (架构 1:1 复刻小规模生产环境,配置略有不同) 主机名 IP CP ...

  7. JVM栈帧

    Java 的源码文件经过编译器编译后会生成字节码文件,然后由 JVM 的类加载器进行加载,再交给执行引擎执行.在执行过程中,JVM 会划出一块内存空间来存储程序执行期间所需要用到的数据,这块空间一般被 ...

  8. 狂神说-Docker基础-学习笔记-02 Docker常用命令

    狂神说-Docker基础-学习笔记-02 Docker常用命令 1.帮助命令 docker version #查看版本信息 docker info #显示docker的系统信息 docker `命令` ...

  9. Redis常见面试题:ZSet底层数据结构,SDS、压缩列表ZipList、跳表SkipList

    文章目录 一.Redis数据结构概述 1.1 Redis有哪些数据类型 1.2 Redis本质是哈希表 1.3 Redis的哈希冲突与渐进式rehash 1.4 数据结构底层 1.4.1 简单动态字符 ...

  10. "安装VMware Tools"显示灰色的解决办法

    用VMware Workstation Pro好几年了,期间这个问题也遇到过好几次,这次把解决方案记录一下,若后续有其他情况其他解决方案将在此博文更新. Step1:关闭虚拟机: Step2:在虚拟机 ...