likes
comments
collection
share

Spring JPA Ⅺ 自给自足之Query详解

作者站长头像
站长
· 阅读数 82

前言

有些同学在使用 JPA 自带的一些接口时,觉得很不自在,浑身不得劲。尤其碰到那些较为复杂的情况,更是心中愤恨:几行 SQL 就能解决的问题,为啥需要这么麻烦呢?别急,JPA 早就为大家准备了自由发挥的空间,那就是注解 @Query,今天就跟大家一起探讨下此注解的用法。

源码

@Documented
public @interface Query {

   /**
    * 指定执行的 JPQL 的查询语句
    */
   String value() default "";

   /**
    * 指定查询 count 的 JPQL 语句;如果不指定将根据 query ⾃动⽣成
    */
   String countQuery() default "";

   /**
    * 根据哪个字段来 count,⼀般默认即可
    */
   String countProjection() default "";

   /**
    * 是否使用原生SQL语句;默认为false
    */
   boolean nativeQuery() default false;

   /**
    * 给 query 设置一个 name;如果没有,则使用{$domainClass}.${queryMethodName}命名
    */
   String name() default "";

   /**
    * 给 count 的 query 设置一个name;如果没有,则使用 {$domainClass}.${queryMethodName}.count 命名
    */
   String countName() default "";
}

JPQL 与 SQL 的区别

JPQL(Java Presistence Query Language)java 持久性查询语言,是面向对象的查询语言,因此它可以完全理解继承、多态和关联等特征。而且 JPQL 内置了大量函数,极大地方便了 JPQL 查询的功能。当然 JPQL 底层依然是基于 SQL 的,但 JPQLSQL 的转换无须开发者关心,JPQL 解析器会负责完成这种转换,并负责执行这种转换的 SQL 语句来更新数据库。 SQL 是面向关系数据库的查询语言,因此 SQL 操作的对象是数据表、数据列;而 JQPL 操作的对象是实体对象,对象属性。

代码对比

JPQL:

//面向对象的 JPQL 语句,其中 User 是实体类
select u from User u

SQL:

//原生 SQL 语句,其中 t_user 是表名
select * from t_user

基本用法

一、JPQL 写法

Dao 层代码
@Query(value = "select u from User u where u.id = ?1")
User findUserByQuery(int id);

控制台打印如下:

Hibernate: 
    select
        user0_.id as id1_2_,
        user0_.age as age2_2_,
        user0_.name as name3_2_ 
    from
        user user0_ 
    where
        user0_.id=?

小贴士

查询语句中的 ? 后面跟的数字代表的是入参的索引(从1开始)

除了这个种写法外,还有一种 @Param 的用法:他是使用 :参数 的方式进行指定查询条件。

个人比较推荐第二种方式,因为这种方式比较清晰明朗,而且有助于后期维护和代码重构。

@Query(value = "select u from User u where u.id = :id")
User findUserByQuery(@Param("id") int id);

控制台打印如下:

Hibernate: 
    select
        user0_.id as id1_2_,
        user0_.age as age2_2_,
        user0_.name as name3_2_ 
    from
        user user0_ 
    where
        user0_.id=?

二、SQL 写法

Dao 层代码
@Query(value = "select * from user where id = ?1", nativeQuery = true)
User findUserByQuery(int id);

控制台打印如下:

Hibernate: 
    select
        * 
    from
        user 
    where
        id = ?

排序用法

Dao 层代码
@Query(value = "select u from User u")
List<User> findAllSortByQuery(Sort sort);
Control 层代码
@GetMapping("findAllSortByQuery")
public List<User> findAllSortByQuery(String sortPara){
    Sort sort = Sort.by( Sort.Direction.DESC, sortPara);
    return userService.findAllSortByQuery(sort);
}

控制台打印如下:

Hibernate: 
    select
        user0_.id as id1_2_,
        user0_.age as age2_2_,
        user0_.name as name3_2_ 
    from
        user user0_ 
    order by
        user0_.age desc

分页用法

Dao 层代码
@Query(value = "select u from User u")
Page<User> findAllPageByQuery(Pageable pageable);
Control 层代码
@GetMapping("findAllPageByQuery")
public Page<User> findAllPageByQuery(){
    Pageable pageable = PageRequest.of(1, 2);
    return userService.findAllPageByQuery(pageable);
}

控制台打印如下:

Hibernate: 
    select
        user0_.id as id1_2_,
        user0_.age as age2_2_,
        user0_.name as name3_2_ 
    from
        user user0_ limit ?,
        ?
Hibernate: 
    select
        count(user0_.id) as col_0_0_ 
    from
        user user0_

countQuery 的使用

countQuery 一般用于分页查询,用来查找页面元素的总个数,其结果用于填充返回的 PagetotalCount 属性。

Dao 层代码
@Query(value = "select * from user where age = 22", countQuery = "select count(*) from user where age = 22", nativeQuery = true)
Page<User> findAllPageByQuery(Pageable pageable);

控制台打印如下:

Hibernate: 
    select
        * 
    from
        user 
    where
        age = 22 limit ?, ?
Hibernate: 
    select
        count(*) 
    from
        user 
    where
        age = 22

小贴士

一般情况下 countQuery 不需要配置,系统会根据 query 自动生成一个 count 查询,其结果为返回的 page 对象 totalCount 属性。

最终总结

其实 @Query 中用的最多的就是 valuenativeQuerycountQuery 的值,其他的基本用不到。还有就是在实际项目中,我们如果使用 JPA 的话,一般都会遵循下面三个原则:

  1. 能⽤⽅法名表示的,尽量⽤⽅法名表示,因为这样语义清晰、简单快速,基本上只要编译通过,⼀定不会有问题
  2. 能⽤ @Query ⾥⾯的 JPQL 表示的,就⽤ JPQL,这样与 SQL ⽆关,万⼀哪天换数据库了,基本上代码不⽤改变
  3. 最后实在没有办法了,可以选择 nativeQuery 写原始 SQL
转载自:https://juejin.cn/post/7231370775150723133
评论
请登录