从零开始 Spring Boot 69:JPA 条件查询

news2024/11/25 11:47:32

从零开始 Spring Boot 69:JPA 条件查询

spring boot

图源:简书 (jianshu.com)

在之前的文章中我们学习过条件查询(Criterial Query),构建条件查询的一般步骤是:

  1. 获取HibernateCriteriaBuilder
  2. 利用HibernateCriteriaBuilder创建JpaCriteriaQuery
  3. 利用JpaCriteriaQuery获取查询的根
  4. 利用HibernateCriteriaBuilder构建谓词
  5. 用谓词组装JpaCriteriaQuery
  6. 利用JpaCriteriaQuery创建Query并执行查询

本篇文章将进一步说明构建条件查询的细节问题。

本文的测试用例将使用下面的实体类:

@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Student {
    public enum Level {
        FRESH_MAN, SOPHOMORE, JUNIOR, SENIOR
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank
    @NotNull
    @Length(max = 45)
    @Column(unique = true)
    private String name;

    @Min(0)
    @Max(100)
    @NotNull
    private Integer averageScore;

    @Enumerated(EnumType.STRING)
    @NotNull
    private Level level;
}

先看一个简单的示例——查找所有的一年级学生:

var cb = sessionFactory.getCriteriaBuilder();
JpaCriteriaQuery<Student> query = cb.createQuery(Student.class);
JpaRoot<Student> root = query.from(Student.class);
JpaPredicate level = cb.equal(root.get("level"), Student.Level.FRESH_MAN);
query = query.where(level);
var students = session.createQuery(query).getResultList();

在这个示例中,用cb.equal构建了一个类似 JPQL 中的s.level=:level这样的查询条件,在这里被称作谓词(Predicate),其类型是JpaPredicate

谓词可以通过JpaCriteriaQuery.where方法绑定到条件查询上。

条件语句

可以用一系列谓词(Predicate)构建查询条件,下面会介绍一些常见的条件谓词。

and

可以用多个谓词组合成更复杂的查询条件,比如下面这个:

var cb = sessionFactory.getCriteriaBuilder();
JpaCriteriaQuery<Student> query = cb.createQuery(Student.class);
JpaRoot<Student> root = query.from(Student.class);
JpaPredicate level = cb.equal(root.get("level"), Student.Level.FRESH_MAN);
JpaPredicate averageScore = cb.ge(root.get("averageScore"), 60);
JpaPredicate and = cb.and(level, averageScore);
query = query.where(and);
var students = session.createQuery(query).getResultList();

这里用了三个谓词,组合成了一个类似 JPQL 中的s.level=:level and s.averageScore>=:score这样的查询条件,用于查询一年级平均分为60分以上的学生。

谓词中的ge表示 greater equal(大于等于),gt表示 greater than(大于),lelt类似。

实际使用中更多的是用级联调用和表达式,并不会为每个谓词创建本地变量,这样可读性会更好:

var cb = sessionFactory.getCriteriaBuilder();
JpaCriteriaQuery<Student> query = cb.createQuery(Student.class);
JpaRoot<Student> root = query.from(Student.class);
query = query.where(cb.and(
    cb.in(root.get("level"), List.of(Student.Level.FRESH_MAN, Student.Level.SOPHOMORE)),
    cb.ge(root.get("averageScore"), 60))
                   );
var students = session.createQuery(query).getResultList();

这个示例查询一年级和二年级所有分数大于等于60分的学生。

or

当然,条件语句并非只有一种写法,比如可以用or来替换in

var cb = sessionFactory.getCriteriaBuilder();
JpaCriteriaQuery<Student> query = cb.createQuery(Student.class);
JpaRoot<Student> root = query.from(Student.class);
query = query.where(cb.and(
    cb.or(
        cb.equal(root.get("level"), Student.Level.FRESH_MAN),
        cb.equal(root.get("level"), Student.Level.SOPHOMORE)),
    cb.ge(root.get("averageScore"), 60))
                   );
var students = session.createQuery(query).getResultList();

和上边的示例具有相同的功能。

between

一般如果我们要查询某个区间的结果,可能会这样组合使用谓词:

var cb = sessionFactory.getCriteriaBuilder();
JpaCriteriaQuery<Student> query = cb.createQuery(Student.class);
JpaRoot<Student> root = query.from(Student.class);
query = query.where(cb.and(cb.ge(root.get("averageScore"), 20),
                           cb.le(root.get("averageScore"), 80)));
var students = session.createQuery(query).getResultList();

JPA 生成的 SQL 语句为:

select s1_0.id,s1_0.average_score,s1_0.level,s1_0.name from student s1_0 where s1_0.average_score>=? and s1_0.average_score<=?

可以使用 between 谓词起到类似的效果:

var cb = sessionFactory.getCriteriaBuilder();
JpaCriteriaQuery<Student> query = cb.createQuery(Student.class);
JpaRoot<Student> root = query.from(Student.class);
query = query.where(cb.between(root.get("averageScore"), 20, 80));
var students = session.createQuery(query).getResultList();

JPA 生成的 SQL 语句:

select s1_0.id,s1_0.average_score,s1_0.level,s1_0.name from student s1_0 where s1_0.average_score between ? and ?

SQL between 操作符的相关介绍说明可以阅读这里。

like

like 也是常用的 SQL 操作符,对应 like 谓词:

var cb = sessionFactory.getCriteriaBuilder();
JpaCriteriaQuery<Student> query = cb.createQuery(Student.class);
JpaRoot<Student> root = query.from(Student.class);
query = query.where(cb.like(root.get("name"), "B%"));
var students = session.createQuery(query).getResultList();

JPA 生成的 SQL 语句:

select s1_0.id,s1_0.average_score,s1_0.level,s1_0.name from student s1_0 where s1_0.name like replace(?,'\\','\\\\')

in

使用 in 可以检索字段是否在某个范围内:

query = query.where(cb.in(root.get("level"), List.of(Student.Level.FRESH_MAN, Student.Level.SOPHOMORE)));

还有另一种写法:

query = query.where(root.get("level").in(Student.Level.FRESH_MAN, Student.Level.SOPHOMORE));

这种写法更接近 SQL 的风格。

isNull

如果字段可以为null,就需要用isNullisNotNull创建查询条件:

query = query.where(cb.isNull(root.get("name")));

JPA 生成的 SQL:

select s1_0.id,s1_0.average_score,s1_0.level,s1_0.name from student s1_0 where s1_0.name is null

isNotNull与之类似,这里不再赘述。

也可以用另一种风格的写法:

query = query.where(root.get("name").isNull());

排序

可以给条件查询添加排序:

query = query.orderBy(cb.desc(root.get("averageScore")),
cb.asc(root.get("name")));

生成的 SQL:

select s1_0.id,s1_0.average_score,s1_0.level,s1_0.name from student s1_0 order by s1_0.average_score desc,s1_0.name

聚合函数

条件语句同样可以使用聚合函数。

比如用count查询数据条目:

var cb = sessionFactory.getCriteriaBuilder();
JpaCriteriaQuery<Long> query = cb.createQuery(Long.class);
JpaRoot<Student> root = query.from(Student.class);
query.select(cb.count(root));
Long count = session.createQuery(query).getSingleResult();
System.out.println(count);

注意,这里的检索结果要映射到一个Long类型的变量,所以cb.createQuery的参数是Long.class,而非一般的Student.class

JPA 生成的 SQL:

select count(s1_0.id) from student s1_0

JPA 生成的 SQL 符合我们一般的编写 SQL 习惯——对主键字段进行计数。

再比如计算平均分总和:

var cb = sessionFactory.getCriteriaBuilder();
JpaCriteriaQuery<Integer> query = cb.createQuery(Integer.class);
JpaRoot<Student> root = query.from(Student.class);
query.select(cb.sum(root.get("averageScore")));
Integer sum = session.createQuery(query).getSingleResult();
System.out.println(sum);

CriteriaUpdate

可以创建一个CriteriaUpdate来构建UPDATE语句,并更新数据:

HibernateCriteriaBuilder cb = sessionFactory.getCriteriaBuilder();
JpaCriteriaUpdate<Student> criteriaUpdate = cb.createCriteriaUpdate(Student.class);
Root<Student> root = criteriaUpdate.from(Student.class);
criteriaUpdate.set("averageScore", 99);
criteriaUpdate.where(cb.equal(root.get("name"), "icexmoon"));
Transaction transaction = session.beginTransaction();
session.createMutationQuery(criteriaUpdate).executeUpdate();
transaction.commit();

JPA 生成的 SQL:

update student set average_score=? where name=?

CriteriaDelete

可以创建一个CriteriaDelete来构建DELETE语句,用于删除数据:

HibernateCriteriaBuilder cb = sessionFactory.getCriteriaBuilder();
JpaCriteriaDelete<Student> criteriaDelete = cb.createCriteriaDelete(Student.class);
Root<Student> root = criteriaDelete.from(Student.class);
criteriaDelete.where(cb.le(root.get("averageScore"), 60));
Transaction transaction = session.beginTransaction();
session.createMutationQuery(criteriaDelete).executeUpdate();
transaction.commit();

The End,谢谢阅读。

可以从这里获取本文的完整示例代码。

参考资料

  • Combining JPA And/Or Criteria Predicates | Baeldung
  • SQL BETWEEN 操作符 | 菜鸟教程 (runoob.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/750668.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

easy rule 学习记录

总体&#xff1a; 使用方面除了官网的wiki外&#xff0c;推荐阅读 作者&#xff1a;夜尽天明_ 链接&#xff1a;https://juejin.cn/post/7048917724126248967 来源&#xff1a;稀土掘金 非annotation 方式&#xff0c;执行不是jdk proxy模式annotation 方式&#xff0c;和ru…

【Linux操作系统】线程控制

文章目录 线程创建线程等待终止线程利用多线程求和(单进程多线程)获取线程ID取消线程线程分离共享&#xff1f; 线程创建 创建线程需要用的函数是pthread_create。函数原型如下&#xff1a; int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start…

left join 和except方法区别和联系

目录 相同点&#xff1a; left join except 不同点 假设有两个表&#xff1a;A客户表 和 B客户表&#xff0c;客户uid是唯一主键 相同点&#xff1a; 查询在A中的客户 但不在B中&#xff0c;也就是图中的阴影部分&#xff0c;left join 和except方法都可以实现 left join …

Harnessing the Power of LLMs in Practice: A Survey on ChatGPT and Beyond

LLM的系列文章&#xff0c;针对《Harnessing the Power of LLMs in Practice: A Survey on ChatGPT and Beyond》的翻译。 在实践中驾驭LLM的力量——ChatGPT及其后的研究综述 摘要1 引言2 模型实用指南2.1 BERT风格的语言模型&#xff1a;编码器-解码器或仅编码器2.2 GPT风格…

python接口自动化(三十五)-封装与调用--流程类接口关联(详解)

简介 流程相关的接口&#xff0c;主要用 session 关联&#xff0c;如果写成函数&#xff08;如上篇&#xff09;&#xff0c;s 参数每个函数都要带&#xff0c;每个函数多个参数&#xff0c;这时候封装成类会更方便。在这里我们还是以博客园为例&#xff0c;带着小伙伴们实践一…

spring复习:(24)ApplicationContext中的BeanPostProcess是在哪里注册到容器的?

在ApplicationContext实现类的构造方法里。 public ClassPathXmlApplicationContext(String configLocation) throws BeansException {this(new String[] {configLocation}, true, null);}上边的构造方法调用如下构造方法 public ClassPathXmlApplicationContext(String[] conf…

ubuntu使用WHEELTE N100并用rviz显示

写在最开头&#xff0c;如果wheeltec n100被自己改动过参数导致无法读取数据&#xff0c;建议在window的上位机中恢复出厂设置并重新上电&#xff0c;在转入ubuntu。因为我就是这个问题&#xff0c;客服远程操控才帮我解决的。 所有官方资料共享&#xff0c;侵删&#xff1a; …

Flink+StarRocks 实时数据分析新范式

摘要&#xff1a;本文整理自 StarRocks 社区技术布道师谢寅&#xff0c;在 Flink Forward Asia 2022 实时湖仓的分享。本篇内容主要分为五个部分&#xff1a; 极速数据分析 实时数据更新 StarRocks Connector For Apache Flink 客户实践案例 未来规划 点击查看原文视频 &a…

一篇文章让你看懂C语言字符函数和内存函数

目录 一、字符函数 1.strlen函数 1.1strlen函数的介绍 1.2strle函数的使用 1.3模拟实现strlen 1.3.1指针移动法 1.3.2指针减去指针法 1.3.3函数递归法 2.strcpy函数 ​编辑 2.1strcpy函数的介绍 2.2strcpy函数的使用 2.3模拟实现strcpy 3.strcat函数 3.1strcat函数的介…

LiveGBS流媒体平台GB/T28181功能-支持海康大华GB28181语音对讲需要的设备及服务准备

LiveGBS支持海康大华GB28181语音对讲需要的设备及服务准备 1、背景2、准备2.1、服务端必备条件&#xff08;注意&#xff09;2.2、准备语音对讲设备2.2.1、 大华摄像机2.2.1.1、 配置接入示例2.2.1.2、 配置音频通道编号 2.2.2、 海康摄像机2.2.2.1、 配置接入示例 3、开启音频…

初试Python路径库

文章目录 一、pathlib概述二、操作路径对象(一)操作属性(二)连接路径(三)拆分完整路径三、路径对象的常用函数(一)获取当前工作目录(二)创建新目录(三)查看主目录一、pathlib概述 自Python 3.4 以来,pathlib一直是标准库的一部分。 PurePath, PurePosixPath, Pure…

即视角|出海资本热土——印尼市场洞察(上)

即视角Insight 共享即构新洞察&#xff0c;共建行业新动能——ZEGO即构科技基于音视频技术领域的多年深耕&#xff0c;综合面向各行业的服务经验&#xff0c;在【即视角】栏目发布即构对行业的洞察。 此前我们根据即构对出海客户的服务经验&#xff0c;输出了文章《即视角&am…

STL标准模板库 set容器

文章目录 迭代器迭代器的五大分类迭代器系列帮手函数一览 set容器打印任意 STL 容器的printer.hset与vectorset 和 vector 的区别set 和 vector 迭代器的共同点set 和 vector 迭代器的不同点 set 的排序set 的排序&#xff1a;string 会按“字典序”来排set 的排序&#xff1a;…

ai智能绘画生成器有哪些?你知道ai生成图片网站哪个好吗?

曾经有一个年轻的画家&#xff0c;名叫亚历克斯。他对艺术充满了热情和渴望&#xff0c;但却常常感到自己的创作灵感有限。每当他拿起画笔&#xff0c;总是困扰于如何将心中的景象完美地呈现在画布上。 有一天&#xff0c;亚历克斯偶然听说了一个神奇的网站&#xff0c;据说这…

【力扣】20. 有效的括号

有效的括号 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 每个右括号都有一个对应的相…

uniapp H5预览PDF文件

1&#xff0c;下载资源后hybrid文件存放在static静态文件里 (点击这里去下载文件) 2&#xff0c;pdf预览页面配置 <template><view style"width: 100vh;"><web-view :src"pdfUrl"></web-view></view> </template><…

【每日一题】1289. 下降路径最小和 II

【每日一题】1289. 下降路径最小和 II 1289. 下降路径最小和 II题目描述解题思路 1289. 下降路径最小和 II 题目描述 给你一个 n x n 整数矩阵 grid &#xff0c;请你返回 非零偏移下降路径 数字和的最小值。 非零偏移下降路径 定义为&#xff1a;从 grid 数组中的每一行选择…

【计算机视觉】简述对EQ-Net的理解

最近又看了一些点云分割的文章&#xff0c;近两年点云分割的文章是真的少&#xff0c;不知道是不是点云分割算法接近了末端。这篇文章主要提出了一个基于查询方法的统一范式&#xff0c;它解决了一些不仅仅是点云分割的问题&#xff0c;还解决了三维点云分类和三维目标检测的问…

解密AI图像安全技术:智能守护数智时代,低代码平台助力圈复杂操作!

前言 随着数智时代的来临&#xff0c;人们进入了一个全新的智能化世界。在这个时代中&#xff0c;人工智能&#xff08;AI&#xff09;成为了一项重要的技术突破&#xff0c;其应用也无处不在。其中&#xff0c;AI图像安全技术作为保障个人和企业数据安全的重要环节&#xff0c…

Linux——认识Linux的目录结构 常用命令 vim命令 权限及其控制

目录 linux的目录结构常用linux的命令ls(list)和llcd 切换目录mkdir 创建文件夹touch命令&#xff1a;创建普通文本文件pwd 显示路径whoamisu&#xff1a;普通--超级账号man&#xff1a;查看手册rm&#xff1a;删除网络命令ifconfig重定向 >>cat 查看文本文件clear清屏hi…