第四章 定义查询方法
作者:互联网
本次主要学习如何利用方法名称定义查询方法。
一、定义查询方法的配置方法
由于Spring JPA Repository的实现原理是采用动态代理的机制,所以我们介绍两种定义查询方法:从方法名称中可以指定特定用于存储的查询和更新,或通过使用@Query手动定义的查询,这个取决于实际存储操作。只需要实体Repository继承Spring Data Common里 面的Repository接口即可,就像前面我们讲的一样。如果你想有其他更多默认通用方法的实现,可以选择JpaRepository、 PagingAndSortingRepository、CrudRepository等接口,也可以直接继承我们后面要讲的JpaSpecificationExecutor、QueryByExampleExecutor和自定义Response,都可以达到同样的效果。
二、查询方法的创建
内部基础架构中有个根据方法名的查询生成器机制,对于在存储库的实体上构建约束查询很有用。该机制方法的前缀有find…By、 read…By、query…By、count…By和get…By,从这些方法可以分析它的其余部分(实体里面的字段)。引入子句可以包含其他表达式, 例如在Distinct要创建的查询上设置不同的标志。然而,第一个By作为分隔符来指示实际标准的开始。在一个非常基本的水平上,你可以定义实体性条件,并与它们串联(And和Or)。 解析方法的实际结果取决于创建查询的持久性存储。但是,有一 些常见的事项需要注意:- 表达式通常是可以连接的运算符的属性遍历。你可以使用组合属性表达式AND和OR。你还可以将运算关键字Between、 LessThan、GreaterThan、Like作为属性表达式。受支持的操 作员可能因数据存储而异,因此请参阅官方参考文档的相应部 分内容。
- 该方法解析器支持设置一个IgnoreCase标志个别特性(例如, findByLastnameIgnoreCase(…))或支持忽略大小写(通常是一个类型的所有属性为String的情况下,例如, findByLastnameAndFirstnameAllIgnoreCase(…))。是否支持忽略示例可能会因存储而异,因此请参阅参考文档中的相关 章节,了解特定于场景的查询方法。
- 可以通过OrderBy在引用属性和提供排序方向(Asc或Desc)的查询方法中附加一个子句来应用静态排序。要创建支持动态排序的查询方法来影响查询结果。
三、关键字列表(需熟练掌握)
以表user(id,name,code,createdate,modifydate) 为例子
关键字 | 示例 | JPQL表达 | 关键字 | 示例 | JPQL表达 |
And | findByidAndname | where id=?1 and name=?2 | Or | findByidOrname | where id =?1 or name=?2 |
Is、Equals | findByid、findByidIs、findByidEquals | where id=?1 | Between | findBycreatedateBetween | where createdate between ?1 and ?2 |
LessThan | findByidLessThan | where id?<?1 | LessThanEqual | findByidLessThanEqual | where id <=?1 |
GreaterThan | findByidGreaterThan | where id?>?1 | GreaterThanEqual | findByidGreaterThanEqual | where id >=?1 |
After | findBycreatedateAfter | where createdate>?1 | Before | findBycreatedateBefore | where createdate<?1 |
IsNull | findByidIsNull | where id is null | IsNotNull,NotNull | findByidIsNotNull | where id is not null |
Like | findByidLike | where id like ?1 | NotLike | findByIdNotLike | where id not like ?1 |
StartingWith | findByidStartingWith | where id like ?1% (参数增加前缀%) | EndingWith | findByidEndingWith | where id like %?1 (参数增加前缀%) |
Containning | findByContaining | where id like %?1% | OrderBy | findByidOrderByid | where id=?! oder by id |
Not | findByidNot | where id<>?1 | In | findByidIn(Colletion<id> ids) | where id in(?1) |
NotIn | findByidNotIn(Colletion<id> ids) | where id not in (?1) | IgnoreCase | fingByidIgnoreCase | where Upper(id)=Upper(?1) |
True | findByActiveTrue() | where id=true | False | findByActiveFalse() | where id=false |
|
四、方法的查询策略的属性表达式
属性表达式(Property Expressions)只能引用托管(泛化)实体的直接属性,如前一个示例所示。在查询创建时,你已经确保解析的属性是托管实体的属性。同时,还可以通过遍历嵌套属性定义约 束。假设一个Person实体对象里面有一个Address属性里面包含一个 ZipCode属性。 在这种情况下,方法名为: 创建及其查找的过程是:解析算法首先将整个part(AddressZipCode)解释为属性,并使用该名称(uncapitalized)检查域类的属性。如果算法成功,就使用该属性。 如果不是,就拆分右侧驼峰部分的信号源到头部和尾部,并试图找出相应的属性,在我们的例子中是AddressZip和Code。如果算法找到一个具有头部的属性,那么它需要尾部,并从那里继续构建树,然后按照刚刚描述的方式将尾部分割。如果第一个分割不匹配,就将分割点移动到左边(Address、ZipCode),然后继续。 虽然这在大多数情况下应该起作用,但是算法可能会选择错误的属性。假设Person类也有一个addressZip属性,该算法将在第一个分割轮中匹配,并且基本上会选择错误的属性,最后失败(因为该类型addressZip可能没有code属性)。 要解决这个歧义,可以在方法名称中手动定义遍历点,所以我们的方法名称最终会是: 当然Spring JPA里面是将下划线视为保留字符,但是强烈建议遵循标准Java命名约定(不使用属性名称中的下划线,而是使用骆驼示例)。命名属性的时候注意一下这个特性。 可以到PartTreeJpaQuery.class查询一下相关的method的name拆分和实现逻辑,也可以利用开发工具的Searchanywhere视图输入PropertyExpression,然后Find Used就可以跟出很多源码,再设置一个断点,就可以进行代码分析了。五、查询结果的处理
1、参数选择分页和排序(Pageable/Sort)
特定类型的参数,动态地将分页和排序应用于查询
示例:在查询方法中使用Pageable、Slice和Sort。
- 第一种方法允许将org.springframework.data.domain.Pageable 实例传递给查询方法,以便动态地将分页添加到静态定义的查询中。 Page知道可用的元素和页面的总数。它通过基础框架里面触发计数查询来计算总数。由于这可能是昂贵的,具体取决于所使用的场景,说白了,当用到Pageable的时候会默认执行一条cout语句。
- 而Slice的作用是,只知道是否有下一个Slice可用,不会执行count,所以当查询较大的结果集时,只知道数据是足够的就可以了,而且相关的业务场景也不用关心一共有多少页。
- 排序选项也通过Pageable实例处理。如果只需要排序,那么在org.springframework.data.domain.Sort参数中添加一个参数即可。(Sort查询也可以单独讲)
- 正如你可以看到的,只返回一个List也是可能的。在这种情况下, Page将不会创建构建实际实例所需的附加元数据(这反过来意味着必须不被发布的附加计数查询),而仅仅是限制查询仅查找给定范围的实体。
2、限制查询结果(Top/First)
示例:在查询方法上加限制查询结果的关键字first和top。- 查询方法的结果可以通过关键字来限制first或top,其可以被互换地使用。可选的数值可以追加到顶部/第一个以指定要返回的最大结果大小。如果数字被省略,则假设结果大小为1。
- 限制表达式也支持Distinct关键字。
- 此外,对于将结果集限制为一个实例的查询,支持将结果包装到一个实例中的Optional中。
- 如果将分页或切片应用于限制查询分页(以及可用页数的计算),则在限制结果中应用。
3、查询结果的不同形式 (List/Stream/Page/Future)
Page和List在上面的示例中都有涉及,下面介绍几种特殊的。
1.流式查询结果 可以通过使用Java 8 Stream<T>作为返回类型来逐步处理查询方法的结果,而不是简单地将查询结果包装在Stream数据存储中,特定的方法用于执行流。 示例:使用Java 8流式传输查询的结果Stream<T>。 提示: 流的关闭问题,try cache 是一种关闭方法。2.异步查询结果
可以使用Spring的异步方法执行功能异步的存储库查询。这意味着方法将在调用时立即返回,并且实际的查询执行将发生在已提交给Spring TaskExecutor的任务中,比较适合定时任务的实际场景。- 使用java.util.concurrent.Future的返回类型。
- 使用java.util.concurrent.CompletableFuture作为返回类型。
- 使用org.springframework.util.concurrent.ListenableFuture作为返回类型。
4、查询结果的不同形式 (List/Stream/Page/Future)
Spring JPA对Projections扩展的支持是非常好的。从字面意思上理解就是映射,指的是和DB查询结果的字段映射关系。一般情况下,返回的字段和DB查询结果的字段是一一对应的,但有的时候,我们需要返回一些指定的字段,不需要全部返回,或者只返回一些复合型的字段,还要自己写逻辑。 Spring Data正是考虑到了这一点,允许对专用返回类型进行建模,以便我们有更多的选择,将部分字段显示成视图对象。 假设Person是一个正常的实体,和数据表Person一一对应,正常的写法如下: (1)我们想仅仅返回其中与name相关的字段,应该怎么做呢? 基于projections的思路,其实是比较容易的。我们只需要声明一个接口,包含要返回的属性的方法即可,例如:
Repository里面的写法如下,直接用这个对象接收结果即可:
在Ctroller里面直接调用对象可以查看结果。原理是运行时底层会有动态代理机制为这个接口生成一个实现实体类。(2)查询关联的子对象,例如:
(3)@Value和SPEL也支持:
PersonRepository里面保持不变,这样会返回一个firstname和lastname相加的只有fullName的结果集合。 (4)对Spel表达式的支持远不止这些: (5)还可以通过Spel表达式取到方法里面的参数值。 (6)这时可能有人会想,只能用interface吗?Dto支持吗?其实也是可以的,我们也可以定义自己的Dto实体类。需要哪些字段,我们直接在Dto类中使用get/set属性即可。例如: (7)支持动态projections。通过泛化,可以根据不同的业务情况返回不同的字段集合。可以对PersonRepository做一定的变化,例如: Projections的应用场景还是挺多的,希望大家好好体会,以利用更优雅的代码实现不同的场景,不必用数组、冗余的对象去接收查询结果。
标签:返回,定义,结果,查询方法,查询,id,第四章,属性 来源: https://blog.csdn.net/yangzhiwei273/article/details/115627484