Spring Data JPA带条件分页查询实现原理
作者:银色甲壳虫 发布时间:2023-07-23 21:32:54
最新Spring Data JPA官方参考手册 Version 2.0.0.RC2,2017-07-25
https://docs.spring.io/spring-data/jpa/docs/2.0.0.RC2/reference/html/
JPA参考手册 (找了半天, 在线版的只找到这个)
https://www.objectdb.com/java/jpa
Spring Data JPA的Specification类, 是按照Eric Evans的《领域驱动设计》书中Specification的概念和语义来定义查询条件的API。
使用Spring Data JPA, 我们一般将自己的dao接口继承CrudRepository接口和JpaSpecificationExecutor接口, 由框架生成代理类来完成具体的调用, 而不用自己写daoImpl实现类, 因为这两个接口自带了很多方法, 如果我们写实现类会发现一上来就需要实现十来个方法, 比较麻烦。
其中CrudRepository接口主要负责增/删/改的操作, JpaSpecificationExecutor接口主要负责查询的操作, 另外, 框架还支持在dao接口的方法名上定义一些简单的语义来进行增删改查, 底层会对应地做具体实现。
那如何封装具体的查询条件呢?
在service层调用dao接口从JpaSpecificationExecutor继承的抽象查询方法, 它就会自动让你准备相关实参, 其中Specification对象就是经常用在条件查询的方法的一个形参, 也就是说, 封装查询条件的过程转移到service层了。
我们一般以匿名内部类的方式new一个Specification对象, 实现其中的toPredicate方法, 举个例子,
Specification<Person> specification = new Specification<Person>() {
@Override
public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
...
};
其中, Predicate, Root, CriteriaQuery, CriteriaBuilder都是javax.persistence包中的接口, 方法的这3个实参由框架交给我们。
Predicate意思是"描述语", 就是我们封装完查询条件后要交给Specification一个清楚的描述, 要怎么组合sql语句去查询。
Root表示为泛型里的Person对象描述一个根位置, 可以从这个根位置去取该对象的属性, 以及属性的属性, 类似对象导航的意思, 比如要取Person地址属性的城市, 就可以root.get("address").get("city").as(String.class), 其返回值是一个Expression对象;
CriteriaQuery代表条件查询,主要提供where、group by、having、order by等。
CriteriaBuilder用于构造筛选条件,主要提供equal、and、or、lt、gt、between、like等, 以及获得CriteriaQuery、CriteriaUpdate、CriteriaDelete对象。构造每个筛选条件一般需要Expression类型作为实参, 可以通过Root对象调用get()方法得到。如果有多个筛选条件, 调用criteriaBuilder的and、or等方法连接起来, 一般是链式调用的形式。
举个简单的实际例子:
//带条件的分页查询, 根据person的first_name和last_name进行模糊查询
//为了直观, 假设两个字段都存在且不为空串, 省掉非空判断和对应的处理
public Page<Person> findSearch(Person person, int page, int size) {
Specification<Person> specification = new Specification<Person>() {
@Override
public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Predicate predicate1 = criteriaBuilder.like(root.get("first_name").as(String.class), "%"+person.getFirstName()+"%");
Predicate predicate2 = criteriaBuilder.like(root.get("last_name").as(String.class), "%"+person.getLastName()+"%");
Predicate finalPredicate = criteriaBuilder.and(predicate1, predicate2);
return finalPredicate;
}
};
PageRequest pageRequest = PageRequest.of(page-1, size);
return personDao.findAll(specification, pageRequest);
}
当然这都是JPQL的语法了, 很多开发者也经常在dao接口中直接写SQL语句来让框架查询, 使用起来感觉有点类似MyBatis, 会显得清爽很多, 只需定义一个抽象方法加上对应的注解@Modifying和@Query(value="sql语句", nativeQuery=true)即可, 它还有一个好处, 不用让封装查询条件这种事情跑到service层去。
来源:https://www.cnblogs.com/sunsay/p/12941863.html


猜你喜欢
- 在阅读本文之前,大家可先参阅《简单理解Spring之IOC和AOP及代码示例》一文,了解下Spring中IOC和AOP的相关内容。下面进入正
- 多线程编程多线程编程模式.NET 中,有三种异步编程模式,分别是基于任务的异步模式(TAP)、基于事件的异步模式(EAP)、异步编程模式(A
- redis redisson 集合操作相关类及接口Rlist:链表public interface RList<V> exten
- 说明Spring Boot Admin 是一个管理和监控你的 Spring Boot 应用程序的应用程序。 这些应用程序通过 Spring
- 前言在java里,当我们需要拷贝一个对象时,有两种类型的拷贝:浅拷贝与深拷贝。浅拷贝只是拷贝了源对象的地址,所以源对象的值发生变化时,拷贝对
- 本文实例为大家分享了C#实现上传下载图片的具体代码,供大家参考,具体内容如下1.首先我们通过流来上传下载图片,所有操作只停留在流这一层Mem
- 虽然闭包主要是函数式编程的玩意儿,而C#的最主要特征是面向对象,但是利用委托或lambda表达式,C#也可以写出具有函数式编程风味的代码。同
- 本文实例为大家分享了Unity3D实现打砖块的具体代码,供大家参考,具体内容如下基于unity20171、 使用Plane创建初始地图(层级
- 背景java程序员一般写的是后端服务是JavaWeb类型的项目,主要包括Http接口和dubbo接口,Http接口一般采用的rest风格,那
- 前言前面我们学习完了设计模式,在其中我们有了解到原型模式。这里涉及到了克隆自身对象。那么也就是对对象进行拷贝。这里就涉及到了这么一个概念。深
- //写注册表RegistryKey regWrite;//往HKEY_CURRENT_USER主键里的Software子键下写一个名为“Te
- 因为我本人很喜欢在不同的页面之间跳转时加点好玩的动画,今天无意间看到一个动画效果感觉不错,几种效果图如下:既然好玩就写在博客中,直接说就是:
- 什么是代理模式?代理模式:在调用处不直接调用目标类进行操作,而是调用代理类,然后通过代理类来调用目标类进行操作。在代理类调用目标类的前后可以
- Service翻译成中文是服务,熟悉Windows 系统的同学一定很熟悉了。A
- 前言 短时间提升自己最快的手段就是背面试题,最近总结了Java常用的面试题,分享给大家,希望大家都能圆梦大厂,加油,我命由我不由天
- 一、管理网络状态使用网络进行数据通信前,需要先获取网络状态。使用ConnectivityManager获取网络状态步骤:1.获取Connec
- package 斐波那契数;import java.util.Scanner;class 斐波那契数 { public static voi
- 1、接口:一种把类抽象的更彻底,接口里只能包含抽象方法的“特殊类”。接口不关心类的内部状态数据,定义的是一批类所遵守的规范。(它只规定这批类
- Java Map.values()方法获取Map集合中的所有键值对象Java 集合类中的 Map.values() 方法用来获取
- 简介本文用示例介绍使用MyBatis-Plus进行多表查询的方法,包括静态查询和动态查询。代码controllerpackage com.e