Spring之Specification复杂查询和Criteria查询

标签: JPA

Specification官网

【一目了然】Spring Data JPA使用Specification动态构建多表查询、复杂查询及排序示例

JPA 使用 Specification 复杂查询和 Criteria 查询

CriteriaBuilder官网

Criteria官网

Criteria-Root官网

JPA criteria 查询:类型安全与面向对象

JPA 2.0 中的动态类型安全查询

java-jpa-criteriaBuilder使用入门

Spring data jpa 的使用与详解(二):复杂动态查询及分页,排序

业务需求:JPA 确实挺好用,给我们提供了 CRUD 的功能,并且用起来也是特别的方便,基本都是一行代码就能完成各种数据库操作,跟 mybatisplus 很像。。。但是在遇到复杂的多条件以及多表查询的时候,总是会首先考虑到手写原生的 SQL。

Specification 算是 JPA 中比较灵活的查询方式,也少不了 Criteria 类型安全和面向对象的优点

环境配置

<!-- jpa -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

代码实现

public class xxxSpecification {

    private xxxSpecification() {
    }

    public static Specification<xxxEntity> buildFromParam(Set<String> xxxList){
		
        return (root,query,builder) -> {
            //Predicate: 过滤条件,相当于构造 where 中的条件
            ArrayList<Predicate> list = new ArrayList<>();
            if(!CollectionUtils.isEmpty(xxxList)){
				//root:从元模型中获取相应的字段
                CriteriaBuilder.In<String> in = builder.in(root.get("id"));
                for(String itemId:xxxList){
                    in.value(itemId);
                }
                list.add(in);
            }

            list.add(builder.equal(root.get("status"), TableStatusEnum.NORMAL_STATUS.getCode()));
            Predicate[] predicates = new Predicate[list.size()];
            predicates = list.toArray(predicates);
            return builder.and(predicates);
        };
    }
}

以上的代码对应于

169

原理

JPA 提供动态接口(JpaSpecificationExecutor),利用类型检查方式,进行复杂的条件查询,这个比自己写 SQL 更加安全

package org.springframework.data.jpa.repository;

import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.Nullable;

public interface JpaSpecificationExecutor<T> {
    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);
}

Specification 是我们传入进去的查询参数,实际上它是一个接口,并且只有一个方法:

public interface Specification<T> {

    Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
}

元模型

在JPA中,标准查询是以元模型的概念为基础的,元模型是为具体持久化单元的受管实体定义的.这些实体可以是实体类,嵌入类或者映射的父类.提供受管实体元信息的类就是元模型类.
简单的说就是元模型是实体类对应的一个“受管实体

例子:

实体类 Employee(com.demo.entities包中定义)

@Entity
@Table
public class Employee{  
    private int id;   
    private String name;
    private int age;
    @OneToMany
    private List<Address> addresses;
    // Other code…
}

Employee类的标准元模型类的名字是 Employee_

import javax.annotation.Generated;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.ListAttribute;
import javax.persistence.metamodel.StaticMetamodel;
@StaticMetamodel(Employee.class)
public class Employee_ {     
    public static volatile SingularAttribute<Employee, Integer> id;   
    public static volatile SingularAttribute<Employee, Integer> age;   
    public static volatile SingularAttribute<Employee, String> name;    
    public static volatile ListAttribute<Employee, Address> addresses;
}

Employee的每一个属性都会使用在JPA2规范中描述的以下规则在相应的元模型类中映射:

  • 元模型类的属性全部是static和public的。
  • 元模型类的属性全部是static和public的。Employee的每一个属性都会使用在JPA2规范中描述的以下规则在相应的元模型类中映射:
  • 对于Addess这样的集合类型,会定义静态属性ListAttribute< A, B> b,这里List对象b是定义在类A中类型B的对象。其它集合类型可以是SetAttribute, MapAttribute 或 CollectionAttribute 类型。

为什么要使用元模型,答:查询类型更加安全

Criteria 查询

为了更好的理解criteria 查询,考虑拥有Employee实例集合的Dept实体,Employee和Dept的元模型类的代码如下:

//All Necessary Imports
@StaticMetamodel(Dept.class)
public class Dept_ {    
    public static volatile SingularAttribute<Dept, Integer> id;   
    public static volatile ListAttribute<Dept, Employee> employeeCollection;    
    public static volatile SingularAttribute<Dept, String> name;
}
//All Necessary Imports
@StaticMetamodel(Employee.class)
public class Employee_ {     
    public static volatile SingularAttribute<Employee, Integer> id;    
    public static volatile SingularAttribute<Employee, Integer> age;    
    public static volatile SingularAttribute<Employee, String> name;    
    public static volatile SingularAttribute<Employee, Dept> deptId;
}

下面的代码片段展示了一个criteria 查询,它用于获取所有年龄大于24岁的员工:

CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Employee> criteriaQuery = criteriaBuilder.createQuery(Employee.class);
Root<Employee> employee = criteriaQuery.from(Employee.class);
Predicate condition = criteriaBuilder.gt(employee.get(Employee_.age), 24);
criteriaQuery.where(condition);
TypedQuery<Employee> typedQuery = em.createQuery(criteriaQuery);
List<Employee> result = typedQuery.getResultList();

对应的SQL: SELECT * FROM employee WHERE age > 24

CriteriaBuilder 安全查询创建工厂

CriteriaBuilder 安全查询创建工厂,创建 CriteriaQuery,创建查询具体条件 Predicate 等。

CriteriaBuilder是一个工厂对象,安全查询的开始.用于构建JPA安全查询.可以从EntityManager 或 EntityManagerFactory类中获得CriteriaBuilder。
比如:

CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();

CriteriaQuery 安全查询主语句

  • 它通过调用 CriteriaBuilder, createQuery 或CriteriaBuilder.createTupleQuery 获得。
  • CriteriaBuilder就像CriteriaQuery 的工厂一样。
  • CriteriaQuery对象必须在实体类型或嵌入式类型上的Criteria 查询上起作用。
  • Employee实体的 CriteriaQuery 对象以下面的方式创建:
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Employee> criteriaQuery = criteriaBuilder.createQuery(Employee.class);

Root 定义查询的 From 子句中能出现的类型

170

查询表达式被赋予泛型。一些典型的表达式是:

  • Root<T>, 相当于一个 From 子句,定义查询的 From 子句中能出现的类型

  • Criteria查询的查询根定义了实体类型,能为将来导航获得想要的结果,它与SQL查询中的FROM子句类似。

  • Root实例也是类型化的,且定义了查询的FROM子句中能够出现的类型。

  • 查询根实例能通过传入一个实体类型给 AbstractQuery.from方法获得

  • Criteria查询,可以有多个查询根。

  • Employee 实体的查询根对象可以用以下语法获得:

    Root<Employee> employee = criteriaQuery.from(Employee.class);
    

源码

package javax.persistence.criteria;

import javax.persistence.metamodel.EntityType;

public interface Root<X> extends From<X, X> {
    EntityType<X> getModel();
}

可以看出 Root 继承自 From

public interface From<Z, X> extends Path<X>, FetchParent<Z, X>

而 From 又继承自 Path

package javax.persistence.criteria;

import java.util.Collection;
import java.util.Map;
import javax.persistence.metamodel.Bindable;
import javax.persistence.metamodel.MapAttribute;
import javax.persistence.metamodel.PluralAttribute;
import javax.persistence.metamodel.SingularAttribute;

public interface Path<X> extends Expression<X> {
    Bindable<X> getModel();

    Path<?> getParentPath();

    <Y> Path<Y> get(SingularAttribute<? super X, Y> var1);

    <E, C extends Collection<E>> Expression<C> get(PluralAttribute<X, C, E> var1);

    <K, V, M extends Map<K, V>> Expression<M> get(MapAttribute<X, K, V> var1);

    Expression<Class<? extends X>> type();

    <Y> Path<Y> get(String var1);
}

Predicate 过滤条件

  • 过滤条件应用到SQL语句的FROM子句中。
  • 在criteria 查询中,查询条件通过Predicate 或Expression 实例应用到CriteriaQuery 对象上。
  • 这些条件使用 CriteriaQuery .where 方法应用到CriteriaQuery 对象上。
  • Predicate 实例也可以用Expression 实例的 isNull, isNotNull 和 in方法获得,复合的Predicate 语句可以使用CriteriaBuilder的and, or andnot 方法构建。
  • CriteriaBuilder 也是作为Predicate 实例的工厂,Predicate 对象通过调用CriteriaBuilder 的条件方法( equal,notEqual, gt, ge,lt, le,between,like等)创建。
  • 这些条件使用 CriteriaQuery .where 方法应用到CriteriaQuery 对象上。

下面的代码片段展示了Predicate 实例检查年龄大于24岁的员工实例:

Predicate condition = criteriaBuilder.gt(employee.get(Employee_.age), 24);
criteriaQuery.where(condition);

初步结论

CriteriaBuilder: 构造 sql 语句

predicate:构造 where 中的条件语句,过滤条件

root:获取对应元模型的字段属性

版权声明:本文为mikelv01原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/mikelv01/article/details/105600508

智能推荐

criteria对象的QBC查询

一.说明 1.获取该对象 session.createCriteria(Class clazz); 2.涉及该对象的方法 createCriteria(Class clazz); 3.参数的说明 实体类的字节码对象。 二.常用方法 1.基本查询 2.条件查询 (1)说明 (2)范例 3.排序查询 4.分页查询 5.统计查询 6.离线查询 (1)说明 DetachedCriteria对象,不需要se...

JPA之条件分页查询(specification),两次调用specification构造

今天在使用JPA分页查询的时候突然发现一个问题   直接上代码,次方法为分页方法,传入参数为当前页(currentPage),pagesize一页多少条,specification条件。 下面写一个生成specification的方法。 然后在调用分页的时候调用改方法,传入参数。是不是觉得没问题了,按理来说是没问题了。 但是!!!!!,有问题,因为我在这个方法中传入的参数为map,map...

spring boot Jpa 按例查询 复杂查询

按例查询即Query by Example (QBE),有时我们字段很多,前端向我们进行数据查询调用时,我们对每个字段写select where and查询语句比较繁琐,利用Jpa的Example可以解决这个问题 举例: User实体 UserRepository: 测试: 结果输出: 多字段查询测试: 结果输出: Example.of方法使用默认的匹配器ExampleMatcher: 默认的匹配...

Web开发 | Hibernate - 13.Hibernate框架的查询方式之HQL、QBC(Criteria查询)

一、Hibernate框架的查询方式 1、唯一标识 OID 的检索方式。session.get(类.class,OID) 2、对象的导航的方式。做一对多的环境搭建的时候,Customer 的 JavaBean 定义了 Set集合 HQL 的检索方式 Hibernate Query Language – Hibernate 的查询语言 QBC 的检索方式 Query By Criteri...

Web开发 | Hibernate - 10.Hibernate框架的查询方式之Criteria查询接口

一、Criteria 查询接口(做条件查询非常合适) QBC:Query By Criteria – 按条件进行查询 QBC 查询方式的使用 先创建查询的接口 Criteria c = session.createCriteria(User.class); 设置查询的条件 criteria.add(Restrictions.gt("age", 10)); 查询数据 ...

猜你喜欢

spring4.2+hibernate5.0.7整合jpa进行增删改查,以及hql查询、sql查询、criteria查询

1.创建项目,导入jar包 这里比spring+hibernate多了个hibernate-entitymanager.5.0.7.jar 2.编写applicationContext.xml jdbc.properties applicationContext.xml 3.编写pojo对象Users Users.java 4.编写dao层和dao.impl层 UsersDao.java User...

Spring4.2整合hibernate5.0.7,进行增删改查,以及hql查询,sql查询,criteria查询

最近学习spring data所有又复习了一下hibernate 开发环境eclipse jdk1.7 Spring4.2.0 +Hibernate5.0.7 1.创建项目,导入jar包 (其中jaxb的三个jar包和javax.activation-api.jar不是必须的,我的没这三个包运行不了,不知道为什么,我的jdk才1.7啊) 然后右键add to build path 2.编写appl...

一文读懂容器三大核心技术——Namespace,Cgroup和UnionFS

本文字数:8814 字 精读时间:18 分钟 也可在 8 分钟内完成速读 容器到底是什么?容器是怎么工作的?容器如何隔离资源?为啥容器启动那么快?...如果你是个好奇宝宝,平时在使用容器的时候内心定会泛起类似疑问。本文将通过讲解其三大核心技术:Linux Namespace,Control Groups(cgroups)和UnionFS (联合文件系统)来解答你心中对容器原理的...

Opencv学习笔记之五——线性邻域滤波(方框滤波、均值滤波、高斯滤波)

首先感谢@浅墨_毛星云,本篇博文是小武通过学习@浅墨_毛星云的博客以及书籍《opencv3.0编程入门》整理的笔记及疑问心得,小武水平有限,欢迎交流。 @浅墨_毛星云博文:https://blog.csdn.net/poem_qianmo/article/category/1923021 【1】方框滤波 方框滤波(box Filter)被封装在一个名为boxblur的函数中,即boxblur函数的...

【雕爷学编程】Arduino动手做(49)---有源蜂鸣器模块

37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和模块,依照实践(动手试试)出真知的理念,以学习和交流为目的,这里准备逐一做做实验,不管能否成功,都会记录下来—小小的进步或是搞不掂的问题,希望能够抛砖引玉。 【Arduino】168种传感器模块系列实验(资料+代码+图形+仿真) 实验四十九:有源蜂鸣器报警...