首页 > 其他分享 >Specifications动态查询

Specifications动态查询

时间:2023-02-16 17:11:48浏览次数:63  
标签:Sort return name Specifications Specification 查询 user 动态

【前言说明】

针对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();
    }
复制代码

 

标签:Sort,return,name,Specifications,Specification,查询,user,动态
From: https://www.cnblogs.com/exmyth/p/17127432.html

相关文章

  • JPA使用Specification构建动态查询
    封装Specification查询条件,在SpringDataJPA2.0以前使用Specifications这个辅助类来操作where、not、and和or连接,在2.0版本以后这个类会被剔除,可以直接使用Specificat......
  • vue3实现动态路由配置
    思路:1、登录成功后,返回动态的路由信息,存储在本地localStorage中。2、router.js中在路由守卫钩子函数中使用router.addRoute()进行动态路由添加。具体代码: login.vue......
  • 从实现到原理,聊聊Java中的SPI动态扩展
    原创:微信公众号码农参上,欢迎分享,转载请保留出处。八股文背多了,相信大家都听说过一个词,SPI扩展。有的面试官就很喜欢问这个问题,SpringBoot的自动装配是如何实现的?基本......
  • sql 查询表数据
    SELECTs.NameASSchemaName,t.NameASTableName,p.rowsASRowCounts--,--CAST(ROUND((SUM(a.used_pages)/128.00),2)ASNUMERIC(36,2))ASUsed_MB,--CAST(R......
  • 快速搭建一个网关服务,动态路由、鉴权,一网打尽!(含流程图)
    作者:热黄油啤酒链接:https://juejin.cn/post/7004756545741258765前言本文记录一下我是如何使用Gateway搭建网关服务及实现动态路由的,帮助大家学习如何快速搭建一个网......
  • 【人工智能】动态时间规整 DTW
    动态时间规整DTWHowDTW(DynamicTimeWarping)algorithmworksdefdtw(seq_a:List,seq_b:List):match_table=[[0for_inrange(len(seq_a))]for_inr......
  • 黑名单查询:前缀树
    引入我现在有一份黑名单数据,里面有10000条域名,现在需要编写一个算法,快速判断一个域名在不在这个黑名单里,怎么设计这个算法?字典树or前缀树前缀树是N叉树的一种根节点......
  • MybatisPlus查询条件设置详解
    select设置需要查询的字段例: 指定查询主键,名字,年龄字段select("id", "name", "age")例: 查询以test开头的属性select(i ‐> i.getProperty().startsWith("t......
  • zlib动态库升级
    阅读目录 1.报错 2.解决方案 3.解决步骤回到顶部 1.报错1/lib64/libz.so.1:version`ZLIB_1.2.9'notfound回到顶部 2.解决方案......
  • 动态代理 动态修改注解上的参数
    动态代理AOP切面代码被代理对象Objecttarget=joinPoint.getTarget();当前对象ObjectaThis=joinPoint.getThis();这里拿到的方法和反射的方法获取的不是一样的......