Spring JPA Ⅺ 自给自足之Query详解
前言
有些同学在使用 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 的,但 JPQL 到 SQL 的转换无须开发者关心,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 一般用于分页查询,用来查找页面元素的总个数,其结果用于填充返回的 Page 的 totalCount 属性。
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
中用的最多的就是 value、nativeQuery、countQuery 的值,其他的基本用不到。还有就是在实际项目中,我们如果使用 JPA 的话,一般都会遵循下面三个原则:
- 能⽤⽅法名表示的,尽量⽤⽅法名表示,因为这样语义清晰、简单快速,基本上只要编译通过,⼀定不会有问题
- 能⽤
@Query
⾥⾯的 JPQL 表示的,就⽤ JPQL,这样与 SQL ⽆关,万⼀哪天换数据库了,基本上代码不⽤改变 - 最后实在没有办法了,可以选择 nativeQuery 写原始 SQL
转载自:https://juejin.cn/post/7231370775150723133