Spring之Specification复杂查询和Criteria查询

您所在的位置:网站首页 standard和specification的区别 Spring之Specification复杂查询和Criteria查询

Spring之Specification复杂查询和Criteria查询

2023-09-19 19:48| 来源: 网络整理| 查看: 265

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 类型安全和面向对象的优点

环境配置 org.springframework.boot spring-boot-starter-data-jpa 代码实现 public class xxxSpecification { private xxxSpecification() { } public static Specification buildFromParam(Set xxxList){ return (root,query,builder) -> { //Predicate: 过滤条件,相当于构造 where 中的条件 ArrayList list = new ArrayList(); if(!CollectionUtils.isEmpty(xxxList)){ //root:从元模型中获取相应的字段 CriteriaBuilder.In 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 { Optional findOne(@Nullable Specification var1); List findAll(@Nullable Specification var1); Page findAll(@Nullable Specification var1, Pageable var2); List findAll(@Nullable Specification var1, Sort var2); long count(@Nullable Specification var1); }

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

public interface Specification { Predicate toPredicate(Root 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 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 id; public static volatile SingularAttribute age; public static volatile SingularAttribute name; public static volatile ListAttribute 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 id; public static volatile ListAttribute employeeCollection; public static volatile SingularAttribute name; } //All Necessary Imports @StaticMetamodel(Employee.class) public class Employee_ { public static volatile SingularAttribute id; public static volatile SingularAttribute age; public static volatile SingularAttribute name; public static volatile SingularAttribute deptId; }

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

CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder(); CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(Employee.class); Root employee = criteriaQuery.from(Employee.class); Predicate condition = criteriaBuilder.gt(employee.get(Employee_.age), 24); criteriaQuery.where(condition); TypedQuery typedQuery = em.createQuery(criteriaQuery); List 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 criteriaQuery = criteriaBuilder.createQuery(Employee.class); Root 定义查询的 From 子句中能出现的类型

170

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

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

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

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

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

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

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

Root employee = criteriaQuery.from(Employee.class); 源码 package javax.persistence.criteria; import javax.persistence.metamodel.EntityType; public interface Root extends From { EntityType getModel(); }

可以看出 Root 继承自 From

public interface From extends Path, FetchParent

而 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 extends Expression { Bindable getModel(); Path getParentPath(); Path get(SingularAttribute var1); Expression get(PluralAttribute var1); Expression get(MapAttribute var1); Expression type(); Path 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:获取对应元模型的字段属性



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3