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);};}
}

以上的代码对应于

原理

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;@OneToManyprivate 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 子句中能出现的类型

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

  • 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:获取对应元模型的字段属性

Spring之Specification复杂查询和Criteria查询相关推荐

  1. (3) Hibernate的查询 标准(Criteria)查询

    Hibernate的查询 标准(Criteria)查询 1 一个简单例子: Java代码   @SuppressWarnings("unchecked") public void ...

  2. Hibernate框架之HQL查询与Criteria 查询的区别

    Hibernate框架提供了HQL查询和Criteria 查询.下面对这两种查询分别做个例子.也好对这两种查询方法有个大概的了解.就用房屋信息表做例子,查询所有房屋信息. HQL语句查询所有房屋信息: ...

  3. Hibernate查询之Criteria查询

    转自:http://www.cnblogs.com/Laupaul/archive/2012/02/15/2353194.html Criteria是一种比hql更面向对象的查询方式.Criteria ...

  4. 391、Java框架46 -【Hibernate - 查询HQL、查询Criteria、查询标准SQL】 2020.10.19

    0.目录 1.HQL 2.使用HQL,根据name进行模糊查询 3.查询Criteria 4.使用Criteria,根据name进行模糊查询 5.查询-标准SQL 6.使用标准SQL,根据name进行 ...

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

    序言 自工作以来,除了以前比较流量的hibernate,就是一直使用ORM 规范 JPA了.而这几天工作需要,研究了下JPA的标准查询,名为:JPA criteria查询.相比JPQL,其优势是类型安 ...

  6. 查询参数HQL实现普通查询及分页查询详解

    题记:写这篇博客要主是加深自己对查询参数的认识和总结实现算法时的一些验经和训教,如果有错误请指出,万分感谢. HQL查询: Criteria查询对查询条件行进了面向对象装封,符合程编员人的思维式方,不 ...

  7. Spring data jpa 条件查询-按时间段查询

    Spring data jpa 条件查询-按时间段查询 @Overridepublic Page<泛型> findRecordList(int couponDetailId, int pa ...

  8. boot spring 接口接收数据_在 Spring Boot 中使用 Dataway 配置数据查询接口

    Dataway介绍 Dataway 是基于 DataQL 服务聚合能力,为应用提供的一个接口配置工具.使得使用者无需开发任何代码就配置一个满足需求的接口. 整个接口配置.测试.冒烟.发布.一站式都通过 ...

  9. Spring Boot之基于Redis实现MyBatis查询缓存解决方案

    转载自 Spring Boot之基于Redis实现MyBatis查询缓存解决方案 1. 前言 MyBatis是Java中常用的数据层ORM框架,笔者目前在实际的开发中,也在使用MyBatis.本文主要 ...

最新文章

  1. linux FreeImage安装编译
  2. python subprocess 模块
  3. 2016 大数据版图
  4. java jvm 内存参数_深入详解JVM内存模型与JVM参数详细配置
  5. 被夸了几十年,地球都要因为它变秃了,你还天天用它......
  6. 如何从JSF获取JSON响应?
  7. 最后解密的两弹元勋,众帅之帅朱光亚
  8. 从零入门Serverless|一文详解Serverless技术选型
  9. C#不支持XPATH2.0
  10. 数学公式识别:基于编码-解码模型
  11. json to graphql schema: json2graphql
  12. linux下mysql 启动命令
  13. Eigen 3.3.7 MatrixVector的运算
  14. rrt matlab算法,rrt算法matlab代码
  15. 系统集成项目管理工程师(中级)考试心得经验
  16. Unity HDRP室外场景打光流程分享(下篇)-白天和夜晚场景打光
  17. windows系统如何真正隐藏文件夹[转载]
  18. 蓝桥杯--输出既是回文又是质数的数
  19. UE5笔记【六】流明引擎Lumen简介;Lumen处理发光物体。
  20. P1139 再分麦粒

热门文章

  1. 机器学习(7)——支持向量机(二):线性可分支持向量机到非线性支持向量机
  2. 2023年PHP常见中高面试题汇总(持续更新)
  3. 工作仅一年就被迫跳槽的感想
  4. Android_线程_多线程下载
  5. Xilinx基于PCIE的部分重配置实现(一)
  6. C++的STL中accumulate函数用法
  7. request 使用方法
  8. 技术支持程序员程序书写规范
  9. 微信公众号多域名回调系统1.0发布
  10. 【基础】Flink -- DataStream API