SpringDataJPA系列(5)@Query应该怎么用?

news2024/11/13 19:36:55

SpringDataJPA系列(5)@Query应该怎么用?

之前说到过,DMQ查询策略有两种:方法命令和@Query注解的方式。为什么需要两种呢?它们分别适用的场景是怎么样的?

@Query使用

定义一个通过名字查询用户的方法
在这里插入图片描述

以下是测试方法:

在这里插入图片描述

QueryLookupStrategy 实现原理

我们可以通过QueryExecutorMethodInterceptor类来进行跟踪和分析,它是查询方法的拦截器,我们在lookupQuery()方法中打个断点。

可以看到显示默认的策略是CreateIfNotFound,也就是如果有@Query注解,那么以@Query的注解内容为准,可以忽略方法名方式。

在这里插入图片描述
我们可以看到strategy.resolveQuery采用了策略模式,它有三种实现策略:

在这里插入图片描述
我们可以看到在解析查询的时候,还有个容错机制,出错后还会采用一次方法名个识别方式进行sql语句的拼接

在这里插入图片描述
那么接着进入到 llookupStratrgy.resolveQuery 方法里面,我们可以看到图中 ①处,如果 Query 注解找到了,就不会走到 ② 处了。

在这里插入图片描述

这时我们点开 Query 里面的 Query 属性的值看一下,你会发现这里同时生成了两个 SQL:一个是查询总数的 Query 定义,另一个是查询结果 Query 定义。

到这里我们已经基本明白了,如果想看看 Query 具体是怎么生成的、上面的 @Param 注解是怎么生效的,可以在上面的图 ① 处 debug 继续往里面看

PS:这里要注意,在Spring启动过的时候,JPA会对资源库的每个方法都进行扫描,然后进行具体查询器RepositoryQuery的选择。

下图是关于RepositoryQuery接口相关类图:

在这里插入图片描述

@Query用法和语法

基本语法

@Query 用法是使用 JPQL 为实体创建声明式查询方法。我们一般只需要关心 @Query 里面的 value 和 nativeQuery、countQuery 的值即可,因为其他的不常用。

  • value:JPQL表达式
  • nativeQuery:JPQL是否是原生的Sql语句
  • countQuery :指定count的JPQL语句,如果不指定将根据query自动生成

使用声明式 JPQL 查询有个好处,就是启动的时候就知道你的语法正确不正确。它的语法结构有点类似我们 SQL:

//查询
SELECT ... FROM ...
[WHERE ...]
[GROUP BY ... [HAVING ...]]
[ORDER BY ...]
//删除
DELETE FROM ... [WHERE ...]
//更新
UPDATE ... SET ... [WHERE ...]

你会发现它的语法结构有点类似我们 SQL,唯一的区别就是 JPQL FROM 后面跟的是对象,而 SQL 里面的字段对应的是对象里面的属性字段

其中“…”省略的部分是实体对象名字和实体对象里面的字段名字,而其中类似 SQL 一样包含的语法关键字有:

SELECT FROM WHERE UPDATE DELETE JOIN OUTER INNER LEFT GROUP BY HAVING FETCH DISTINCT OBJECT NULL TRUE FALSE NOT AND OR BETWEEN LIKE IN AS UNKNOWN EMPTY MEMBER OF IS AVG MAX MIN SUM COUNT ORDER BY ASC DESC MOD UPPER LOWER TRIM POSITION CHARACTER_LENGTH CHAR_LENGTH BIT_LENGTH CURRENT_TIME CURRENT_DATE CURRENT_TIMESTAMP NEW EXISTS ALL ANY SOME

用法案例
  • 单条件查询
  @Query("select u from User u where u.emailAddress = ?1")
  User findByEmailAddress(String emailAddress);
  • LIKE查询
  @Query("select u from User u where u.firstname like %?1")
  List<User> findByFirstnameEndsWith(String firstname);
  • 原始sql查询,nativeQuery = true 即可,注意nativeQuery 不支持直接 Sort 的参数查询
  @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
  User findByEmailAddress(String emailAddress);
  • nativeQuery 排序的正确写法
@Query(value = "select * from user_info where first_name=?1 order by ?2",nativeQuery = true)
List<UserInfoEntity> findByFirstName(String firstName,String sort);
//调用的地方写法last_name是数据里面的字段名,不是对象的字段名
repository.findByFirstName("jackzhang","last_name");
  • JPQL排序
  @Query("select u from User u where u.lastname like ?1%")
  List<User> findByAndSort(String lastname, Sort sort);
  @Query("select u.id, LENGTH(u.firstname) as fn_len from User u where u.lastname like ?1%")
  List<Object[]> findByAsArrayAndSort(String lastname, Sort sort);

//调用方的写法,如下:
repo.findByAndSort("lannister", new Sort("firstname"));
repo.findByAndSort("stark", new Sort("LENGTH(firstname)"));
repo.findByAndSort("targaryen", JpaSort.unsafe("LENGTH(firstname)"));
repo.findByAsArrayAndSort("bolton", new Sort("fn_len"));
  • JQPl 的排序
  @Query(value = "select u from User u where u.lastname = ?1")
  Page<User> findByLastname(String lastname, Pageable pageable);
//调用者的写法
repository.findByFirstName("jackzhang",new PageRequest(1,10));
  • nativeQuery 的排序
   @Query(value = "select * from user_info where first_name=?1 /* #pageable# */",
         countQuery = "select count(*) from user_info where first_name=?1",
         nativeQuery = true)
   Page<UserInfoEntity> findByFirstName(String firstName, Pageable pageable);
}
//调用者的写法
return userRepository.findByFirstName("jackzhang",new PageRequest(1,10, Sort.Direction.DESC,"last_name"));
//打印出来的sql
select  *   from  user_info  where  first_name=? /* #pageable# */  order by  last_name desc limit ?, ?

这里需要注意:这个注释 / #pageable# / 必须有。

  • 根据 firstname 和 lastname 参数查询 user 对象
  @Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
  User findByLastnameOrFirstname(@Param("lastname") String lastname,  @Param("firstname") String firstname);
  • 根据 firstname 和 lastname 参数查询 user 对象,并带上限制返回
  @Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
  User findTop10ByLastnameOrFirstname(@Param("lastname") String lastname, @Param("firstname") String firstname);

@Param 注解指定方法参数的具体名称,通过绑定的参数名字指定查询条件,这样不需要关心参数的顺序。比较推荐这种做法,因为它比较利于代码重构

@Query最佳实践

使用场景:映射返回指定过的DTO

新增一个实体表
在这里插入图片描述
原来的用户表
在这里插入图片描述

当我们需要查询用户的名称、部队、主帅技能时应该如何操作?

  • 小白写法,查询获得的对象后再塞到DTO中
   @Query("select u.name,u.email,e.idCard from User u,UserExtend e where u.id= e.userId and u.id=:id")
   List<Object[]> findByUserId(@Param("id") Long id);
  • 进阶写法:定义个返回dto,@Query中构建返回dto直接返回

查询方法的实现,注意红色标注部分是实现关键

下面是测试代码:
在这里插入图片描述
注意:我们在构建返回的时候还可以使用CONCAT 的关键字做了一个字符串拼接,这对一些统一的返回处理还是有好处的,但不建议太复杂的计算。

我们可以在ParameterizedFunctionExpression 类中看到支持的关键字

在这里插入图片描述

  • 高阶写法:定义一个返回接口,@Query中构建返回dto直接返回
    在这里插入图片描述

@Query的查询写法如下:

在这里插入图片描述

测试方法如下:

在这里插入图片描述
比起 DTO 我们不需要 new 了,并且接口只能读,那么我们返回的结果 DTO 的职责就更单一了,只用来查询。接口的方式是比较推荐的做法,因为它是只读的,对构造方法没有要求,返回的实际是 HashMap。

@Query动态查询

在这里插入图片描述
通过上面的实例可以看得出来,我们采用了 :email isnullor s.email = :email 这种方式来实现动态查询的效果,实际工作中也可以演变得很复杂。

总结

  • 能用方法名表示的,尽量用方法名表示,因为这样语义清晰、简单快速,基本上只要编译通过,一定不会有问题
  • 能用 @Query 里面的 JPQL 表示的,就用 JPQL,这样与 SQL 无关,万一哪天换数据库了,基本上代码不用改变
  • 最后实在没有办法了,可以选择 nativeQuery 写原始 SQL,特别是一开始从 MyBatis 转过来的同学,选择写 SQL 会更容易一些

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

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

相关文章

将泛型和函数式编程结合,竟然会让代码这么优雅!

但这种方式却太表象了&#xff0c;没有灵魂和深度&#xff0c;过去的那些日子&#xff0c;我感觉自己的编程水平也就限于把重复的代码抽一抽&#xff0c;&#xff08;如下图所示一样&#xff09;&#xff0c;甚至觉得代码优化不就是这样吗&#xff0c;这样的状态一直维持很久。…

Linux多线程——利用C++模板对pthread线程库封装

文章目录 线程封装主要框架线程启动线程等待其他信息 测试函数 线程封装 我们之前介绍过pthread的线程库&#xff0c;这个线程库主要是基于C语言的void*指针来进行传参和返回 我们使用C的模板对其封装可以让他的使用更加方便&#xff0c;并且经过测试可以让我们更加直观的了解…

DPDK基础入门(二):Cache与大页优化

Cache简介 目前Cache主要由三级组成: L1 Cache, L2 Cache和Last Level Cache(LLC)。 L1最快&#xff0c;但容量小&#xff0c;可能只有几十KB。LLC慢&#xff0c;但容量大&#xff0c;可能多达几十MB。 L1和L2 Cache一般集成在CPU内部。另外,&#xff0c;L1和L2 Cache是每个处…

【2024】Datawhale X 李宏毅苹果书 AI夏令营 Task3

本文是关于李宏毅苹果书”第2章 实践方法论“学习内容的记录。 模型在测试集上表现不佳&#xff0c;可能是因为模型没有充分学习训练集。模型不能充分学习训练集的原因&#xff1a; 模型偏差优化问题过拟合不匹配 一、模型偏差 模型偏差是指&#xff1a;由于模型过于简单&a…

网站如何针对不同的DDOS进行防御?

建设网站租用服务器是多数企业及个人的选择&#xff0c;一个安全稳定的服务器对网站的重要性无需再赘述。要保证服务器租用的安全和稳定&#xff0c;除了需要服务器自身有强大的硬、软件基础之外&#xff0c;还需要防范外部的一些因素&#xff0c;常见的就是各种网络攻击&#…

Linux 上如何做MySQL数据备份

目录 SQL备份脚本创建crontabcrontab命令总结查看特定目录中的周期性任务 crontab&#xff08;cron table 的缩写&#xff09;是 Unix/Linux 系统上用于设置周期性被执行的任务的工具。它允许用户定义需要在特定时间&#xff08;比如每天凌晨、每周的某个时间等&#xff09;自动…

驭势科技研究成果入选学术顶会IROS 2024

近日&#xff0c;驭势科技团队关于自动驾驶车辆定位算法的最新研究成果《LiDAR-based HD Map Localization using Semantic Generalized ICP with Road Marking Detection》&#xff0c;创造性地解决了基于LiDAR的实时路标检测和高精地图配准所带来的挑战&#xff0c;成功入选国…

汇川技术|KingIOServer与AC810PLC通过ModbusTCP通讯测试

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 最近有个项目用亚控的KingSCADA软件开发SCADA系统&#xff0c;需要和汇川的AC810PLC进行通讯&#xff1b; 本节测试亚控的采集软件KingIOServer与汇川的AC810PLC的通讯测试。 以下为测试笔记。 01 效果演示 测试过程…

3个苹果锁屏密码解锁方法,帮你快速解决密码忘记的烦恼!

苹果手机锁屏密码忘记了是一件很常见的问题&#xff0c;但也是一件让人头疼的事情。如果你遇到了这样的问题&#xff0c;不要着急&#xff0c;因为有很多方法可以帮助你解锁iPhone。下面我们将介绍四种简单的方法来解锁iPhone。 一、使用密码解锁工具 iphone忘记了密码怎么解锁…

iconfont图标字体库详细介绍

概述 图标库在前端开发中应用十分广泛&#xff0c;图标库不仅会丰富美化界面的展示&#xff0c;语义化的图标库更能简洁明了地向用户传达某些信息&#xff0c;比如功能的特性和作用&#xff0c;引导用户&#xff0c;极大提高系统的易用性。在没有 UI 设计师的情况下&#xff0…

【C++】手动实现队列的封装(C++)

目录 源代码&#xff1a; 输出结果如下&#xff1a; 实现以下封装 源代码&#xff1a; #include <iostream>using namespace std;class Queue { private:int* arr; // 队列的动态数组int front; // 队列头部元素的索引int rear; // 队列尾部元素的索引in…

新版某数字壳脱壳,过frida检测,及重打包

目录 脱壳 寻找特征& frida hook 过frida检测 修复dex 重打包 修改smail 去签名校验 正文 大家好&#xff0c;我是小生&#xff0c;这次的app是一个国内某计划app, 功能相当全&#xff0c;界面也很美观&#xff0c;很实用&#xff0c;这个app我很欣赏。总共花了有…

【SQL】Delete使用

目录 语法 需求 示例 分析 代码 语法 DELETE删除表中所需内容 删除表中满足特点条件的行&#xff1a;DELETE FROM 表名 WHERE 条件; 删除表中所有行&#xff1a;DELETE FROM 表名; WHERE子句 WHERE子句用于指定从表中选取记录的条件。允许筛选数据&#xff0c;只返回满足…

【文献精读】基于驱动力表的无人车终端无约束预测纵向控制(TVT)

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

ElasticSearch学习笔记(六)自动补全、拼音分词器、RabbitMQ实现数据同步

文章目录 前言11 自动补全11.1 拼音分词器11.2 自定义分词器11.3 自动补全查询 12 数据同步12.1 实现方案12.1.1 同步调用12.1.2 异步通知12.1.3 监听binlog 12.2 异步通知实现数据同步12.2.1 声明交换机和队列12.2.2 发送MQ消息12.2.3 接收MQ消息并操作ES 前言 ElasticSearch…

数据结构————单向链表

头插&#xff1a; 尾插&#xff1a; 头删&#xff1a; 尾删&#xff1a;

一种常用嵌入式开发代码库

链接&#xff1a;https://gitee.com/zhangxinyuanqi/varch 使用开源协议&#xff1a;GPL-2.0 varch简介 varch&#xff08;we-architecture&#xff0c;意为我们的框架库&#xff09;是嵌入式C语言常用代码模块库&#xff0c;包含了嵌入式中常用的算法库, 数据结构&#xff…

JPA关联MyBatis

3.1 JPA 多表查询 多表查询在 Spring Data JPA 中有两种实现方式&#xff0c;第一种是创建一个结果集的接口来接受多表连接查询后的结果&#xff0c;第二种是利用 JPA 的关联映射来实现 3.1.1 数据库表及关系 CRM 数据库中除 sys_user(用户)表外&#xff0c;还包括sys_role(角…

触想内嵌式工业一体机应用于智能检票机改善旅游体验

一、行业发展背景 每年下半年&#xff0c;暑假、中秋、国庆总是接踵而至&#xff0c;随之而来的出游高峰一波接一波。凶猛需求之下&#xff0c;各地景区、游乐园客流压力加大&#xff0c;特别在检票环节&#xff0c;人工检票效率低、秩序混乱&#xff0c;导致常常出现检票口人山…

POL(Point-of-Load)负载点电源

负载点&#xff08;POL&#xff09;电源在靠近负载处单独放置电源调节器(线性稳压器或DC-DC)&#xff0c;解决了高性能半导体器件&#xff0c;例如&#xff1a;微控制器、ASIC等&#xff0c;所面临的高峰值电流、低噪声裕量等设计挑战。 一般我们会把负载点电源尽量靠近负载放…