Spring Data JPA 在 @Query 中使用投影的方法
Spring Data JPA 在 @Query 中使用投影的方法
关于投影的基本使用可以参考这篇文章:https://www.baeldung.com/spring-data-jpa-projections。下文沿用了这篇文章中的示例代码。
投影的官方文档链接是:https://docs.spring.io/spring-data/jpa/docs/2.6.5/reference/html/#projections (我这里使用的是 2.6.5 的版本)。
背景铺垫完毕,接下来开始正文。
最近在写需求的时候用到了投影来减少数据库查询的字段,结果发现官方文档中挖了个坑= =。官方文档中以及另一篇示例文章中,全程使用了方法名派生的查询方式,而投影的文档中却全程没有提到示例的内容仅在方法名派生的查询方式下才有效。
那么,方法名派生的查询方式好用吗?对于简单的只有两三个字段的查询来说,确实方便好用,但条件一多,问题就来了,如果有五六个字段要过滤,那方法名简直长的不能看,并且很多查询默认值都需要通过参数传进来而不是直接内置到 SQL 中。
在这种时候我更偏好使用自定义查询的方式,直接面向 SQL 编程,比看巨长的方法名要容易的多。
当我在这次需求中把投影和自定义查询一结合,这坑它就来了...
上面提过,使用投影是为了减少数据库查询的字段。而直接运行示例代码的时候也确实看到了这个效果:
测试代码
@Test
public void whenUsingOpenProjections_thenViewWithRequiredPropertiesIsReturned() {
PersonView personView = personRepository.findByLastName("Doe");
}
public interface PersonView {
String getLastName();
}
@Entity
public class Person {
@Id
private Long id;
private String firstName;
private String lastName;
}
执行的 SQL
select person0_.last_name as col_0_0_ from person person0_ where person0_.last_name=?
然后当我换成自定义查询的方式时,效果就变成了这样:
测试代码
@Query("select p from Person p where p.lastName = ?1")
PersonView findByLastNameByQuery(String lastName);
@Test
public void whenUsingOpenProjections_thenViewWithRequiredPropertiesIsReturned2() {
PersonView personView = personRepository.findByLastNameByQuery("Doe");
}
执行的SQL
select person0_.id as id1_6_, person0_.first_name as first_na2_6_, person0_.last_name as last_nam3_6_ from person person0_ where person0_.last_name=?
可以看到这里是查询了全部的字段(实在是让人摸不着头脑)。
后来有同事提醒说是因为我写了select p导致的,我就尝试写明要查询的字段(但还是无法理解为什么在这种情况下投影直接不生效):
测试代码
@Query("select p.lastName from Person p where p.lastName = ?1")
PersonView findByLastNameByQuery(String lastName);
执行的 SQL
select person0_.last_name as col_0_0_ from person person0_ where person0_.last_name=?
从 SQL 上来看,这样写已经是实现了我想要的效果,可是实际上真正使用这个代码的时候,坑就又来了:
测试代码
@Test
public void whenUsingOpenProjections_thenViewWithRequiredPropertiesIsReturned2() {
PersonView personView = personRepository.findByLastNameByQuery("Doe");
assertThat(personView.getLastName()).isEqualTo("Doe");
}
加了一行断言来模拟使用的场景
执行结果
org.opentest4j.AssertionFailedError:
expected: "Doe"
but was: null
Expected :"Doe"
Actual :null
直接黑人问号脸。
分析了一下,执行的 SQL 没有问题,投影类也没有问题,那问题就是出在结果集映射的时候了。虽然没看过 JPA 的代码,但是最终肯定是基于 JDBC API 的,而JDBC API是怎么处理结果集映射的?
翻一翻 ResultSet 类可以看到一共有两种方法获取结果:by index 和 by name,仔细看看执行的 SQL,person0_.last_name as col_0_0_ last_name 自动生成了一个别名叫col_0_0_,而投影类中能获得的信息只有字段名last_name而没有别名col_0_0_,所以 by name 的路走不通;
那么by index呢,很明显也不行,我这里的示例只有一个字段,假如有两个字段,那么SQL 中的字段的顺序和投影类中的字段的顺序就无法保证一致,从而就无法根据 index 来获取想要的对应的结果。
然后就是验证环节了,假如是因为名字映射不上导致的结果为 null,那我就给你一个能对应的名字:
测试代码
@Query("select p.lastName as lastName from Person p where p.lastName = ?1")
PersonView findByLastNameByQuery(String lastName);
@Test
public void whenUsingOpenProjections_thenViewWithRequiredPropertiesIsReturned2() {
PersonView personView = personRepository.findByLastNameByQuery("Doe");
assertThat(personView.getLastName()).isEqualTo("Doe");
}
执行的 SQL
select person0_.last_name as col_0_0_ from person person0_ where person0_.last_name=?
虽然执行的 SQL 上还是用了自动生成的别名,但是断言却通过了,猜测是 JPA 在解析 Query 的时候存储了手动声明的别名信息。
最后总结一下,如果要在 @Query 中使用投影,必须要主动声明要查询的字段,并且主动写明字段的别名才行。
最后的最后,再吐槽一下 JPA,文档中提到投影除了基于接口之外,还可以基于类来实现,然鹅当你想在 @Query 中使用基于类的投影时,~。
Spring Data JPA 在 @Query 中使用投影的方法的更多相关文章
- 【spring data jpa】repository中使用@Query注解使用hql查询,使用@Param引用参数,报错:For queries with named parameters you need to use provide names for method parameters. Use @Param for query method parameters, or when on
在spring boot中, repository中使用@Query注解使用hql查询,使用@Param引用参数 如题报错: For queries with named parameters you ...
- spring data jpa 利用@Query进行查询
参照https://blog.csdn.net/yingxiake/article/details/51016234#reply https://blog.csdn.net/choushi300/ar ...
- 【spring data jpa】使用spring data jpa时,关于service层一个方法中进行【删除】和【插入】两种操作在同一个事务内处理
场景: 现在有这么一个情况,就是在service中提供的一个方法是先将符合条件的数据全部删除,然后再将新的条件全部插入数据库中 这个场景需要保证service中执行两步 1.删除 2.插入 这两步自然 ...
- Spring Data JPA之@Query注解
比如有个实体类对象,类名为Book,对应数据表的表名为book 1. 一个使用@Query注解的简单例子:占位符?1和?2 @Query(value = "select name,autho ...
- Spring Data Jpa使用@Query注解实现模糊查询(LIKE关键字)
/** * * @param file_name 传入参数 * @return */ @Query(value = "select * from user where name LIKE C ...
- 关于spring data jpa的@query的传入参数是对象怎么匹配参数
/** * Specifies methods used to obtain and modify person related information * which is stored in th ...
- Spring Data Jpa 使用@Query标注自定义查询语句
https://blog.csdn.net/daniel7443/article/details/51159865 https://blog.csdn.net/pp_fzp/article/detai ...
- Spring boot 中Spring data JPA的应用(一)
最近一直在研究Spring Boot,今天为大家介绍下Spring Data JPA在Spring Boot中的应用,如有错误,欢迎大家指正. 先解释下什么是JPA JPA就是一个基于O/R映射的标准 ...
- Spring Boot 入门系列(二十七)使用Spring Data JPA 自定义查询如此简单,完全不需要写SQL!
前面讲了Spring Boot 整合Spring Boot JPA,实现JPA 的增.删.改.查的功能.JPA使用非常简单,只需继承JpaRepository ,无需任何数据访问层和sql语句即可实现 ...
随机推荐
- 干货 | LVM快照学习
一个执着于技术的公众号 前言 在上一章节,我们学习了LVM逻辑卷管理技术,知道了LVM能够通过增减PE的数量来弹性调整文件系统的大小.除此之外,LVM还有另一个重要功能「LVM快照技术」,也就是可以给 ...
- 详解 Java 17 中新推出的密封类
Java 17推出的新特性Sealed Classes经历了2个Preview版本(JDK 15中的JEP 360.JDK 16中的JEP 397),最终定稿于JDK 17中的JEP 409.Seal ...
- Golang 实现 Redis(11): RDB 文件解析
RDB 文件使用二进制方式存储 Redis 内存中的数据,具有体积小.加载快的优点.本文主要介绍 RDB 文件的结构和编码方式,并借此探讨二进制编解码和文件处理方式,希望对您有所帮助. 本文基于 RD ...
- 目标检测复习之Anchor Free系列
目标检测之Anchor Free系列 CenterNet(Object as point) 见之前的过的博客 CenterNet笔记 YOLOX 见之前目标检测复习之YOLO系列总结 YOLOX笔记 ...
- 《回炉重造 Java 基础》——集合(容器)
整体框架 绿色代表接口/抽象类:蓝色代表类. 主要由两大接口组成,一个是「Collection」接口,另一个是「Map」接口. 前言 以前刚开始学习「集合」的时候,由于没有好好预习,也没有学好基础知识 ...
- AI趋势量化系统(Binance升级版)
更多精彩内容,欢迎关注公众号:数量技术宅,也可添加技术宅个人微信号:sljsz01,与我交流. B圈大跌行情,如何应对? 近期,伴随着美联储持续的加息进程,数字货币市场不论是市场焦点LUNA,还是BT ...
- HCNP Routing&Switching之VRRP基础
前文我们了解了链路高可用技术链路聚合相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/16279078.html:今天我们来聊一聊另一种高可用技术,网关高 ...
- 【Java面试】请说一下Mysql索引的优点和缺点?
今天分享的这道面试题,让一个工作4年的小伙子去大众点评拿了60W年薪. 这道面试题是: "请你说一下Mysql索引的优点和缺点" 关于这道题,看看普通人和高手的回答 普通人: 嗯. ...
- MyBatis - SqlSessionFactory 与 SqlSession
SqlSessionFactory SqlSessionFactory是创建SqlSession的工厂,一般使用单例模式,不需要重复创建. SqlSession SqlSession是直接与数据库直接 ...
- Web自动化定位方法以及常用便捷操作
很遗憾现在才开始给大家逐步分享自动化教程,原本计划着将现有的接口以及app.pc网页端进行自动化处理后再逐步给大家好好分享一下,由于当前实在没必要自动化操作了,所以临时用脑海中的知识再为大家继续更一篇 ...