@Query 疑难杂症

简介: 关于注解Query的一些疑难问题谈一点

快速体验 @Query 的方法

开始之前,首先来看一个 Demo,沿用我们之前的例子,新增一个 @Query 的方法,快速体验一下 @Query 的使用方法,如下所示:

package com.example.jpa.example1;

import org.springframework.data.jpa.repository.JpaRepository;

import org.springframework.data.jpa.repository.Query;

import org.springframework.data.repository.query.Param;

public interface UserDtoRepository extends JpaRepository<User,Long> {

   //通过query注解根据name查询user信息

   @Query("From User where name=:name")

   User findByQuery(@Param("name") String nameParam);

}

然后,我们新增一个测试类:

package com.example.jpa.example1;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

@DataJpaTest

public class UserRepositoryQueryTest {

   @Autowired

   private UserDtoRepository userDtoRepository;

   @Test

   public void testQueryAnnotation() {

//新增一条数据方便测试      userDtoRepository.save(User.builder().name("jackxx").email("123456@126.com").sex("man").address("shanghai").build());

      //调用上面的方法查看结果

      User user2 = userDtoRepository.findByQuery("jack");

      System.out.println(user2);

   }

}

最后,看到运行的结果如下:

Hibernate: insert into user (address, email, name, sex, version, id) values (?, ?, ?, ?, ?, ?)

Hibernate: select user0_.id as id1_0_, user0_.address as address2_0_, user0_.email as email3_0_, user0_.name as name4_0_, user0_.sex as sex5_0_, user0_.version as version6_0_ from user user0_ where user0_.name=?

User(id=1, name=jack, email=123456@126.com, version=0, sex=man, address=shanghai)

通过上面的例子我们发现,这次不是通过方法名来生成查询语法,而是 @Query 注解在其中起了作用,使 "From User where name=:name"JPQL 生效了。那么它的实现原理是什么呢?我们通过源码来看一下。

JpaQueryLookupStrategy 关键源码剖析

那么我们来看下源码是如何起作用的

我们先打开 QueryExecutorMethodInterceptor 类,找到如下代码:

image.png

再运行上面的测试用例,这时候在这里设置一个断点,可以看到默认的策略是CreateIfNotFound,也就是如果有@Query注解,那么以@Query的注解内容为准,可以忽略方法名。

我们继续往后面看,进入到 lookupStrategy.resolveQuery 里面,如下所示:

image.png

通过上图的断点和红框之处,我们也发现了,Spring Data JPA 这个地方使用了策略、模式,当我们自己写策略模式的时候也可以进行参考。

那么接着往下 debug,进入到 resolveQuery 方法里面,如下图所示:

image.png

我们可以看到图中 ①处,如果 Query 注解找到了,就不会走到 ② 处了(即我们第 03 课时中讲的 Defined Query Method 语法)。

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

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

image.png

我们继续一路 debug 就可以看到怎么通过 @Query 去生成 SQL 了,这个不是本节的重点,我在这里就简单带过了,你有兴趣可以自己去 debug 看一下。

那么原理我们掌握了,接下来看看 @Query 给我们提供了哪些语法吧,先看下基本用法。

@Query 的基本用法

在讲解它的语法之前,我们看一下它的注解源码,了解一下基本用法。

package org.springframework.data.jpa.repository;

public @interface Query {

   /**

    * 指定JPQL的查询语句。(nativeQuery=true的时候,是原生的Sql语句)

    */

   String value() default "";

   /**

    * 指定count的JPQL语句,如果不指定将根据query自动生成。

    * (如果当nativeQuery=true的时候,指的是原生的Sql语句)

    */

   String countQuery() default "";

   /**

    * 根据哪个字段来count,一般默认即可。

    */

   String countProjection() default "";

   /**

    * 默认是false,表示value里面是不是原生的sql语句

    */

   boolean nativeQuery() default false;

   /**

    * 可以指定一个query的名字,必须唯一的。

    * 如果不指定,默认的生成规则是:

    * {$domainClass}.${queryMethodName}

    */

   String name() default "";

   /*

    * 可以指定一个count的query的名字,必须唯一的。

    * 如果不指定,默认的生成规则是:

    * {$domainClass}.${queryMethodName}.count

    */

   String countName() default "";

}

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

使用声明式 JPQL 查询有个好处,就是启动的时候就知道你的语法正确不正确。那么我们简单介绍一下 JPQL 语法。

JPQL 的语法

我们先看一下查询的语法结构,代码如下:

SELECT ... FROM ...

[WHERE ...]

[GROUP BY ... [HAVING ...]]

[ORDER BY ...]

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

同理我们看一下 update 和 delete 的语法结构:

DELETE FROM ... [WHERE ...]

 

UPDATE ... SET ... [WHERE ...]

其中“...”省略的部分是实体对象名字和实体对象里面的字段名字,而其中类似 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 这么多,我们就不一一介绍了。

相关文章
|
7月前
|
SQL 关系型数据库 MySQL
OBCP第四章 SQL调优-Hint
OBCP第四章 SQL调优-Hint
198 0
|
7月前
|
SQL 数据库 索引
阿里P8整理出SQL笔记:收获不止SOL优化抓住SQL的本质
开头我先说: 有人就有江湖,有江湖就有IT系统,有IT系统就有数据库,有数据库就有SQL,SQL应用可一字概括:“"广"。加之其简单易学,SQL实现也可一字概括:“乐”。
|
4月前
|
存储 SQL 缓存
解锁MSSQL存储过程优化之道:参数化查询的技术深度探究
解锁MSSQL存储过程优化之道:参数化查询的技术深度探究
|
SQL 关系型数据库 MySQL
老司机总结的12条 SQL 优化方案(非常实用)(二)
老司机总结的12条 SQL 优化方案(非常实用)(二)
老司机总结的12条 SQL 优化方案(非常实用)(二)
|
SQL 数据挖掘 关系型数据库
由一次 UPDATE 过慢 SQL 优化而总结出的经验
由一次 UPDATE 过慢 SQL 优化而总结出的经验
由一次 UPDATE 过慢 SQL 优化而总结出的经验
|
SQL 存储 关系型数据库
一次非常有意思的 SQL 优化经历:从 30248.271s 到 0.001s
场景 我用的数据库是mysql5.6,下面简单的介绍下场景
一次非常有意思的 SQL 优化经历:从 30248.271s 到 0.001s
|
SQL JSON 算法
庖丁解牛-图解查询分析和调优利器Optimizer Trace
查询分析和调优背景在数据库的使用过程,我们经常会碰到SQL突然变慢,是由于下面导致的问题:选择错误的索引选择错误的连接顺序范围查询使用了不同的快速优化策略子查询选择的执行方式变化半连接选择的策略方式变化如何使用分析工具OPTIMIZER TRACE来理解优化过程和原理来深度分析和调优慢SQL就成为高级DBA的利器。我们先来了解下查询优化整体的过程:该过程主要考虑的因素是访问方式、连接顺序和方法已经
874 1
庖丁解牛-图解查询分析和调优利器Optimizer Trace