当前位置: 首页 > article >正文

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

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.kler.cn/a/289287.html

相关文章:

  • 软件工程师简历(精选篇)
  • 软件测试:测试用例详解
  • 【HarmonyOS NEXT】一次开发多端部署(以轮播图、Tab栏、列表为例,配合栅格布局与媒体查询,进行 UI 的一多开发)
  • JSON-RPC-CXX深度解析:C++中的远程调用利器
  • MySQL45讲 第二十讲 幻读是什么,幻读有什么问题?
  • 【初阶数据结构与算法】链表刷题之移除链表元素、反转链表、找中间节点、合并有序链表、链表的回文结构
  • QT connect的使用
  • 算法练习题11:单词出现次数
  • Android kotlin使用Netty网络框架实践(客户端、服务端)
  • 新版Pycharm的Available Packages里面为空,新版没有Manage Repositories功能,如何解决
  • OpenGL/GLUT实践:弹簧-质量-阻尼系统模拟摆动的绳子和布料的物理行为(电子科技大学信软图形与动画Ⅱ实验)
  • 《React Hooks:让你的组件更灵活》
  • Android之电量优化
  • 【论文笔记】Multi-Task Learning as a Bargaining Game
  • 4.3 python 编辑单元格
  • 惠中科技:开启综合光伏清洗新征程
  • 文件包含所用协议实战
  • sql-labs56-60通关攻略
  • 设计模式结构型模式之适配器模式
  • vue3子组件修改父组件传来的值
  • 普元Devops-在云主机上拉取harbor的docker镜像并部署
  • 2017年系统架构师案例分析试题五
  • JVM理论篇(一)
  • Flask的secret_key作用
  • Nginx负载均衡数据流分析
  • ES6 类-总结