SpringBoot使用JpaRepository方法命名和@Query查询的一些复杂场景
一.背景
公司安排了师带徒的任务,我个人也经历了JSP文件中写SQL、数据库视图和存储过程、JDBC写SQL、Dorado、ibatis、mybatis、Hibernate的HQL、JPA的JPQL、JPA的CriteriaQuery等各类跟关系数据打交道的组件语言。最近几年利用SpringBoot的JpaRepository方法命名查询和@Query注解查询多一些,今天总结一下。
二.JpaRepository方法命名
1.先看个我的复杂需求
简单的,大家都容易会,我想从一个复杂的需求来入手,快速了解掌握方法命名查询的要点。我的需求是这样的,想根据某个属性大于等于一个值并取最小的1条,我的方法命名是这样的:
Optional<HistoryDayData> findFirstByDayGreaterThanEqualOrderByDayAsc(@Param("day") LocalDate day);
JpaRepository完美的理解到了我的意图。生成出来的sql如下:
select
historyday0_.id,
......
from
history_day_data historyday0_
where
historyday0_.day>=?
order by
historyday0_.day asc limit ?
跟预期一致。
2.官方的标准资料
参考JPA的Repository详解_jparepository-CSDN博客,我们再来学习一下别人详细的方法命名查询内容:
Keyword | Sample | JPQL snippet |
---|---|---|
And | findByNameAndPwd | where name= ? and pwd =? |
Or | findByNameOrSex | where name= ? or sex=? |
Is,Equals | findById,findByIdEquals | where id= ? |
Between | findByIdBetween | where id between ? and ? |
LessThan | findByIdLessThan | where id < ? |
LessThanEquals | findByIdLessThanEquals | where id <= ? |
GreaterThan | findByIdGreaterThan | where id > ? |
GreaterThanEquals | findByIdGreaterThanEquals | where id > = ? |
After | findByIdAfter | where id > ? |
Before | findByIdBefore | where id < ? |
IsNull | findByNameIsNull | where name is null |
isNotNull,NotNull | findByNameNotNull | where name is not null |
Like | findByNameLike | where name like ? |
NotLike | findByNameNotLike | where name not like ? |
StartingWith | findByNameStartingWith | where name like ‘?%’ |
EndingWith | findByNameEndingWith | where name like ‘%?’ |
Containing | findByNameContaining | where name like ‘%?%’ |
OrderBy | findByIdOrderByXDesc | where id=? order by x desc |
Not | findByNameNot | where name <> ? |
In | findByIdIn(Collection<?> c) | where id in (?) |
NotIn | findByIdNotIn(Collection<?> c) | where id not in (?) |
True | findByAaaTue | where aaa = true |
False | findByAaaFalse | where aaa = false |
IgnoreCase | findByNameIgnoreCase | where UPPER(name)=UPPER(?) |
top | findTop10 | top 10/where ROWNUM <=10 |
3.其他特殊的想法
(1)如果查询要返回一条就是Optional<xxx> findFirstByXXXXX
(2)如果查询要返回分页就Page<xxx> findByxxxxx(xxxx,...,Pageable pageable)
(3)匹配One端关联对象的属性怎么办?OneToOne和ManyToOne的One端
HistoryDayData与Stock是ManyToOne的关系,Many端中One端的属性首字母大写哦!我这里属性的名字是stock,我想匹配它内部的code属性,所以我是ByStockCode。
Optional<HistoryDayData> findFirstByStockCodeAndDayGreaterThanEqualOrderByDayAsc(String code,LocalDate day );
生成的SQL如下:
select
historyday0_.id ,
......
from
history_day_data historyday0_
left outer join
stock stock1_
on historyday0_.stock_id=stock1_.id
where
stock1_.code=?
and historyday0_.day>=?
order by
historyday0_.day asc limit ?
(4)匹配Many端关联对象的属性怎么办?OneToMany和ManyToMany的Many端
Stock与HistoryDayData是OneToMany的关系,One端中Many端的属性首字母大写哦!我查询Stock,想匹配其historyDayDatas内部的Day属性,我写的就是ByHistoryDayDatasDay。
Optional<Stock> findByHistoryDayDatasDay(LocalDate day);
生成的SQL如下:
select
stock0_.id,
......
from
stock stock0_
left outer join
history_day_data historyday1_
on stock0_.id=historyday1_.stock_id
where
historyday1_.day=?
4.无法实现的情况
一般,某个查询方法会提供几个查询条件,如果用户没有输入某个查询条件就忽略该属性的匹配。这样的需求,方法命名就实现不了啦。。。。。。得用@Query了。
三.@Query的使用
如果希望是没有输入day就忽略对day属性的匹配就应该为如下:
@Query(" select p from HistoryDayData p where (:day is null or p.day>=:day) order by p.day asc ")
四.总结
JpaRepository方法命名和@Query查询各有所长。对用户查询使用的建议@Query吧,需求怪异的多些。内部模型间逻辑的用JpaRepository方法命名查询还是很高效的,可读性也很强。