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 indexby 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 中使用投影的方法的更多相关文章

  1. 【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 ...

  2. spring data jpa 利用@Query进行查询

    参照https://blog.csdn.net/yingxiake/article/details/51016234#reply https://blog.csdn.net/choushi300/ar ...

  3. 【spring data jpa】使用spring data jpa时,关于service层一个方法中进行【删除】和【插入】两种操作在同一个事务内处理

    场景: 现在有这么一个情况,就是在service中提供的一个方法是先将符合条件的数据全部删除,然后再将新的条件全部插入数据库中 这个场景需要保证service中执行两步 1.删除 2.插入 这两步自然 ...

  4. Spring Data JPA之@Query注解

    比如有个实体类对象,类名为Book,对应数据表的表名为book 1. 一个使用@Query注解的简单例子:占位符?1和?2 @Query(value = "select name,autho ...

  5. Spring Data Jpa使用@Query注解实现模糊查询(LIKE关键字)

    /** * * @param file_name 传入参数 * @return */ @Query(value = "select * from user where name LIKE C ...

  6. 关于spring data jpa的@query的传入参数是对象怎么匹配参数

    /** * Specifies methods used to obtain and modify person related information * which is stored in th ...

  7. Spring Data Jpa 使用@Query标注自定义查询语句

    https://blog.csdn.net/daniel7443/article/details/51159865 https://blog.csdn.net/pp_fzp/article/detai ...

  8. Spring boot 中Spring data JPA的应用(一)

    最近一直在研究Spring Boot,今天为大家介绍下Spring Data JPA在Spring Boot中的应用,如有错误,欢迎大家指正. 先解释下什么是JPA JPA就是一个基于O/R映射的标准 ...

  9. Spring Boot 入门系列(二十七)使用Spring Data JPA 自定义查询如此简单,完全不需要写SQL!

    前面讲了Spring Boot 整合Spring Boot JPA,实现JPA 的增.删.改.查的功能.JPA使用非常简单,只需继承JpaRepository ,无需任何数据访问层和sql语句即可实现 ...

随机推荐

  1. python中的sort用法

    内置的列表类型提供sort的方法 可以根据多项指标给list实例中的元素排序.在默认情况下,sort方法总是按照自然升序排列列表内的元素 #升序排列 list1=[2,3,1,2,5] list1.s ...

  2. 项目实战:rsync+sersync实现数据实时同步

    一.组网介绍 本次实验使用两台主机: qll251 角色:Rsync server + Sersync server qll252 角色: Rsync client 本次实验采用CentOS7.7系统 ...

  3. 关于div及display

    1.DIV div被看作是一个盒子,可以设置width.height.这个盒子其实是由三部分构成width(height).padding.border.在默认情况下,所见到的div是border和p ...

  4. Redis 为何使用近似 LRU 算法淘汰数据,而不是真实 LRU?

    在<Redis 数据缓存满了怎么办?>我们知道 Redis 缓存满了之后能通过淘汰策略删除数据腾出空间给新数据. 淘汰策略如下所示: 设置过期时间的 key volatile-ttl.vo ...

  5. spring-boot rest controller 使用枚举作为参数,重写反序列化实现任意值转枚举类型

    目录 BaseEnum MyEnum StringToEnumConverterFactory FormatterConfig DTO RestController 参考 BaseEnum packa ...

  6. MySQL的Explain总结

    Explain简介 MySQL优化器在基于成本的计算和基于规则的SQL优化会生成一个所谓的执行计划,我们就可以使用执行计划查看MySQL对该语句具体的执行方式. 介绍这个好啰嗦就是了,我们可以通过这个 ...

  7. 理“ Druid 元数据”之乱

    vivo 互联网大数据团队-Zheng Xiaofeng 一.背景 Druid 是一个专为大型数据集上的高性能切片和 OLAP 分析而设计的数据存储系统. 由于Druid 能够同时提供离线和实时数据的 ...

  8. GraphX 图计算实践之模式匹配抽取特定子图

    本文首发于 Nebula Graph Community 公众号 前言 Nebula Graph 本身提供了高性能的 OLTP 查询可以较好地实现各种实时的查询场景,同时它也提供了基于 Spark G ...

  9. vue华视电子身份证阅读器的使用

          ie还是谷歌都是可以用的 只需要直接启用华视电子身份证阅读器的服务来的,至于服务已经上传到了网上   华视阅读器服务,下载下来解压,找到对应的华视电子读卡服务.exe文件,路径是CVR-1 ...

  10. SSMS设置为深色模式

    更新记录 2022年4月16日:本文迁移自Panda666原博客,原发布时间:2022年2月8日. 2022年4月16日:SSMS很好用,但现在我更多使用DataGrip了. 2022年6月11日:S ...