SpringDataJPA(12)Specifications动态查询:概述

1. 概述

当给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。
相比JPQL,其优势是类型安全,更加的面向对象。

在其源码里面,定义了许多方法,与JpaRepository接口里面的方法差不多

JpaSpecificationExecutor里面的查询方法的参数是Specification< T> spec;由此可以动态去拼接不同的查询条件以调用方法的显示去完成需求

2. JpaSpecificationExecutor方法介绍

T findOne(Specification<T> spec);  //查询单个对象

List<T> findAll(Specification<T> spec);  //查询列表

//查询全部,分页
//pageable:分页参数
//返回值:分页pageBean(page:是springdatajpa提供的)
Page<T> findAll(Specification<T> spec, Pageable pageable);

//查询列表
//Sort:排序参数
List<T> findAll(Specification<T> spec, Sort sort);

long count(Specification<T> spec);//统计查询

Specification :查询条件,这是一个接口

  • 需要自定义我们自己的Specification实现类

  • 实现方法:

    • Predicate toPredicate(Root root, CriteriaQuery<?> query, CriteriaBuilder cb); //用于封装查询条件
    • root:查询的根对象(查询的任何属性都可以从根对象中获取)
    • CriteriaQuery:顶层查询对象,自定义查询方式(作为一个了解,一般不用)
    • CriteriaBuilder:查询的构造器,封装了很多的查询条件

3. 测试动态查询

3.1 测试环境搭建

该项目的测试环境与之前(SpringDataJPA(6)SpringDataJPA概述与使用SpringDataJPA进行CRUD操作)的一样,在这里便不再继续叙述了
在这里的dao层的内容如下:

package cn.yy.dao;

import cn.yy.domain.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

/**
 * @author Marston
 * @date 2021/12/5
 *
 *      符合SpringDataJpa的dao层接口规范
 *              JpaRepository<操作的实体类类型,实体类中主键属性的类型>
 *                  封装了基本CRUD操作”
 *              JpaSpecificationExecutor<操作的实体类类型>
 *                  封装了复杂查询(分页)
 */
public interface SpecCustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {
    
}

3.2 查询单个对象

测试类中的代码:

package cn.yy.test;

import cn.yy.dao.CustomerDao;
import cn.yy.dao.SpecCustomerDao;
import cn.yy.domain.Customer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.*;
import java.util.Arrays;
import java.util.List;

/**
 * @author Marston
 * @date 2021/12/5
 */
@RunWith(SpringJUnit4ClassRunner.class)//声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml")//指定spring容器的配置信息
public class SpecTest {
    @Autowired
    private SpecCustomerDao specCustomerDao;

    /**
     * 根据条件查询单个对象:
     *      根据客户名查询:
     *          查询条件:
     *              1.查询方式
     *                  cb对象
     *              2.比较的属性名称
     *                  root对象
     */
    @Test
    public void testSpec(){
        /*匿名内部类,自定义查询条件
            1.实现Specification接口(提供泛型:查询的对象类型)
            2.实现toPredicate方法(构造查询条件)
            3.需要借助方法参数中的两个参数(
                    root:获取需要查询的对象属性
                    CriteriaBuilder:构造查询条件的,内部封装了很多的查询条件(模糊匹配,精准匹配。)
               )
         */
        Specification<Customer> spec = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                //1.获取比较的属性:参数是属性名
                Path<Object> custName = root.get("custName");
                //2.构造查询条件:select * from cst_customer where cust_name = '更新后的结果'
                /*
                        第一个参数:需要比较的属性(path对象)
                        第二个参数:当前需要比较的取值
                 */
                Predicate predicate = criteriaBuilder.equal(custName, "更新后的结果");//进行精准的匹配(比较的属性,比较的是属性的取值)
                return predicate;
            }
        };
        Customer one = specCustomerDao.findOne(spec);
        System.out.println(one);
    }

}

运行结果:

在这里插入图片描述

3.3 完成多条件拼接

测试类中的代码,该测试是在3.2中的测试类中运行的:

/**
     * 多条件查询:
     *      根据客户名和客户所属行业进行查询
     */
    @Test
    public void testSpec1(){
        /*
        root:获取属性
            客户名
            所属行业
         cb:构造查询
            1.构造客户名的精准匹配查询
            2.构造所属行业的精准匹配查询
            3.将以上两个查询联系起来
         */
        Specification<Customer> spec = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Path<Object> custName = root.get("custName");//客户名
                Path<Object> custIndustry = root.get("custIndustry");//所属行业
                //构造查询
                //1.构造客户名的精准匹配查询
                Predicate p1 = criteriaBuilder.equal(custName, "延迟");
                //2.构造所属行业的精准匹配查询
                Predicate p2 = criteriaBuilder.equal(custIndustry, "学习");
                //3.将以上两个查询联系起来(组合在一起):and、or
                Predicate and = criteriaBuilder.and(p1, p2);//and():以与的形式拼接多个查询条件;cb.or():以或的形式拼接多个查询条件

                return and;
            }
        };
        //是使用findOne还是findAll是要根据数据库中数据的内容而定
        Customer one = specCustomerDao.findOne(spec);
        System.out.println(one);
    }

运行结果:

在这里插入图片描述

3.4 模糊匹配查询列表

测试类中的代码,该测试是在3.2中的测试类中运行的:

/**
     * 根据客户名称的模糊匹配
     *      equal():直接的到path对象(属性),然后进行比较即可
     *      gt、lt、ge、le、like:这些方法不能直接去比较,
     *                          需要先得到path对象,根据path指定比较的参数类型,再去进行比较
     *                          指定参数类型:path.as(类型的字节码对象)
     */
    @Test
    public void testSpec3(){
        //构造查询条件
        Specification<Customer> spec = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                //查询属性:客户名
                Path<Object> custName = root.get("custName");
                //查询方式:模糊查询
                Predicate predicate = criteriaBuilder.like(custName.as(String.class), "延%");
                return predicate;
            }
        };
        List<Customer> all = specCustomerDao.findAll(spec);
        for (Customer customer :all){
            System.out.println(customer);
        }
    }

运行结果:

在这里插入图片描述

3.5 排序

测试类中的代码,该测试是在3.2中的测试类中运行的:

/**
     * 根据客户名称的模糊匹配
     *      equal():直接的到path对象(属性),然后进行比较即可
     *      gt、lt、ge、le、like:这些方法不能直接去比较,
     *                          需要先得到path对象,根据path指定比较的参数类型,再去进行比较
     *                          指定参数类型:path.as(类型的字节码对象)
     */
    @Test
    public void testSpec3(){
        //构造查询条件
        Specification<Customer> spec = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                //查询属性:客户名
                Path<Object> custName = root.get("custName");
                //查询方式:模糊查询
                Predicate predicate = criteriaBuilder.like(custName.as(String.class), "延%");
                return predicate;
            }
        };
        /*创建排序对象,需要调用构造方法实例化Sort对象
                第一个参数:排序的顺序(倒序,正序)
                    Sort.Direction.DESC:倒序
                    Sort.Direction.ASC:升序
                第二个参数:排序的属性名称
         */
        Sort sort = new Sort(Sort.Direction.DESC,"custId");
        List<Customer> all = specCustomerDao.findAll(spec,sort);
        for (Customer customer :all){
            System.out.println(customer);
        }
    }

运行结果:

在这里插入图片描述

3.6 分页

测试类中的代码,该测试是在3.2中的测试类中运行的:

/**
     * 分页查询:
     *      findAll(Specification, Pageable):带有条件的分页
     *          Specification:查询条件
     *          Pageable:分页参数
     *              分页参数: 查询的页码,每页查询的条数
     *      findAll(Pageable):没有条件的分页
     *
     *      返回: Page对象: (这个对象是springDataJpa为我们封装好的pageBean对象,包括:数据列表、共条数)
     */
    @Test
    public void testSpec4(){
        Specification<Customer> spec = null;
        /*
        创建PageRequest的过程中,需要调用他的构造方法传入两个参数
                第一个参数:当前查询的页数(从0开始)
                第二个参数:每页查询的数量
         */
        Pageable pageable = new PageRequest(0,2);
        Page<Customer> all = specCustomerDao.findAll(spec, pageable);
        System.out.println("数据列表集合:"+all.getContent());
        System.out.println("得到总条数:"+all.getTotalElements());
        System.out.println("得到总页数:"+all.getTotalPages());
    }

运行结果:

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

?abc!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值