参数选择(Sort/Pageable)分页和排序

特定类型的参数,Pageable 并动态 Sort 地将分页和排序应用于查询

案例:在查询方法中使用 Pageable、Slice 和 Sort。

Page<User> findByLastname(String lastname, Pageable pageable);
Slice<User> findByLastname(String lastname, Pageable pageable);
List<User> findByLastname(String lastname, Sort sort);
List<User> findByLastname(String lastname, Pageable pageable);

第一种方法允许将 org.springframework.data.domain.Pageable 实例传递给查询方法,以动态地将分页添加到静态定义的查询中,Page 知道可用的元素和页面的总数,它通过基础框架里面触发计数查询来计算总数。由于这可能是昂贵的,这取决于所使用的场景,说白了,当用到 Pageable 的时候会默认执行一条 cout 语句。而 Slice 的用作是,只知道是否有下一个 Slice 可用,不会执行count,所以当查询较大的结果集时,只知道数据是足够的,而相关的业务场景也不用关心一共有多少页。

排序选项也通过 Pageable 实例处理,如果只需要排序,需在 org.springframework.data.domain.Sort 参数中添加一个参数即可,正如看到的,只需返回一个 List 也是可能的。在这种情况下,Page 将不会创建构建实际实例所需的附加元数据(这反过来意味着必须不被发布的附加计数查询),而仅仅是限制查询仅查找给定范围的实体。

限制查询结果

案例:在查询方法上加限制查询结果的关键字 First 和 top。

User findFirstByOrderByLastnameAsc();
User findTopByOrderByAgeDesc();
Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);
Slice<User> findTop3ByLastname(String lastname, Pageable pageable);
List<User> findFirst10ByLastname(String lastname, Sort sort);
List<User> findTop10ByLastname(String lastname, Pageable pageable);

查询方法的结果可以通过关键字来限制 first 或 top,其可以被可互换使用,可选的数值可以追加到顶部/第一个以指定要返回的最大结果的大小。如果数字被省略,则假设结果大小为 1,限制表达式也支持 Distinct 关键字。此外,对于将结果集限制为一个实例的查询,支持将结果包装到一个实例中 Optional。如果将分页或切片应用于限制查询分页(以及可用页数的计算),则在限制结果中应用。

查询结果的不同形式(List/Stream/Page/Future)

Page 和 List 在上面的案例中都有涉及下面将介绍的几种特殊的方式。

流式查询结果

可以通过使用 Java 8 Stream<T> 作为返回类型来逐步处理查询方法的结果,而不是简单地将查询结果包装在 Stream 数据存储中,特定的方法用于执行流。

示例:使用 Java 8 流式传输查询的结果 Stream<T>。

@Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();
Stream<User> readAllByFirstnameNotNull();
@Query("select u from User u")
Stream<User> streamAllPaged(Pageable pageable);

注意:流的关闭问题,try catch 是一种用关闭方法。

Stream<User> stream;
try {stream = repository.findAllByCustomQueryAndStream()stream.forEach(…);
} catch (Exception e) {e.printStackTrace();
} finally {if (stream!=null){stream.close();}
}

异步查询结果

可以使用 Spring 的异步方法执行功能异步执行存储库查询,这意味着方法将在调用时立即返回,并且实际的查询执行将发生在已提交给 Spring TaskExecutor 的任务中,比较适合定时任务的实际场景。

@Async
Future<User> findByFirstname(String firstname); (1)
@Async
CompletableFuture<User> findOneByFirstname(String firstname); (2)
@Async
ListenableFuture<User> findOneByLastname(String lastname);(3)
  • 使用 java.util.concurrent.Future 的返回类型。
  • 使用 java.util.concurrent.CompletableFuture 作为返回类型。
  • 使用 org.springframework.util.concurrent.ListenableFuture 作为返回类型。

所支持的返回结果类型远不止这些,可以根据实际的使用场景灵活选择,其中 Map 和 Object[] 的返回结果也支持,这种方法不太推荐使用,应为没有用到对象思维,不知道结果里面装的是什么。

下表列出了 Spring Data JPA Query Method 机制支持的方法的返回值类型。

某些特定的存储可能不支持全部的返回类型。 只有支持地理空间查询的数据存储才支持 GeoResult、GeoResults、GeoPage 等返回类型。

而我们要看引用的那个 Spring Data 的实现子模块,以 Spring Data JPA 为例,看看 JPA 默认帮实现了哪些返回值类型。

还是通过工具分析 JpaRepository 帮我们实现了哪些返回类型,这样不至于直接看官方文档的时候一头雾水。

Projections 对查询结果的扩展

Spring JPA 对 Projections 的扩展的支持,个人觉得这是个非常好的东西,从字面意思上理解就是映射,指的是和 DB 的查询结果的字段映射关系。一般情况下,我们是返回的字段和 DB 的查询结果的字段是一一对应的,但有的时候,需要返回一些指定的字段,不需要全部返回,或者返回一些复合型的字段,还得自己写逻辑。Spring Data 正是考虑到了这一点,允许对专用返回类型进行建模,以便更有选择地将部分视图对象。

假设 Person 是一个正常的实体,和数据表 Person 一一对应,我们正常的写法如下:

@Entity
class Person {@IdUUID id;String firstname, lastname;Address address;@Entitystatic class Address {String zipCode, city, street;}
}
interface PersonRepository extends Repository<Person, UUID> {Collection<Person> findByLastname(String lastname);
}

(1)但是我们想仅仅返回其中的 name 相关的字段,应该怎么做呢?如果基于 projections 的思路,其实是比较容易的。只需要声明一个接口,包含我们要返回的属性的方法即可。如下:

interface NamesOnly {String getFirstname();String getLastname();
}

Repository 里面的写法如下,直接用这个对象接收结果即可,如下:

interface PersonRepository extends Repository<Person, UUID> {Collection<NamesOnly> findByLastname(String lastname);
}

Ctroller 里面直接调用这个对象可以看看结果。

原理是,底层会有动态代理机制为这个接口生产一个实现实体类,在运行时。

(2)查询关联的子对象,一样的道理,如下:

interface PersonSummary {String getFirstname();String getLastname();AddressSummary getAddress();interface AddressSummary {String getCity();}
}

(3)@Value 和 SPEL 也支持:

interface NamesOnly {@Value("#{target.firstname + ' ' + target.lastname}")String getFullName();…
}

PersonRepository 里面保持不变,这样会返回一个 firstname 和 lastname 相加的只有 fullName 的结果集合。

(4)对 Spel 表达式的支持远不止这些:

@Component
class MyBean {String getFullName(Person person) {…//自定义的运算}
}
interface NamesOnly {@Value("#{@myBean.getFullName(target)}")String getFullName();…
}

(5)还可以通过 Spel 表达式取到方法里面的参数的值。

interface NamesOnly {@Value("#{args[0] + ' ' + target.firstname + '!'}")String getSalutation(String prefix);
}

(6)这时候有人会在想,只能用 interface 吗?dto 支持吗?也是可以的,也可以定义自己的 Dto 实体类,需要哪些字段我们直接在 Dto 类当中暴漏出来 get/set 属性即可,如下:

class NamesOnlyDto {private final String firstname, lastname;
//注意构造方法NamesOnlyDto(String firstname, String lastname) {this.firstname = firstname;this.lastname = lastname;}String getFirstname() {return this.firstname;}String getLastname() {return this.lastname;}
}

(7)支持动态 Projections,想通过泛化,根据不同的业务情况,返回不通的字段集合。

PersonRepository做一定的变化,如下:

interface PersonRepository extends Repository<Person, UUID> {Collection<T> findByLastname(String lastname, Class<T> type);
}

我们的调用方,就可以通过 class 类型动态指定返回不同字段的结果集合了,如下:

void someMethod(PersonRepository people) {
//我想包含全字段,就直接用原始entity(Person.class)接收即可Collection<Person> aggregates = people.findByLastname("Matthews", Person.class);
//如果我想仅仅返回名称,我只需要指定Dto即可。Collection<NamesOnlyDto> aggregates = people.findByLastname("Matthews", NamesOnlyDto.class);
}

最后,Projections 的应用场景还是挺多的,望大家好好体会,这样可以实现更优雅的代码,去实现不同的场景。不必要用数组,冗余的对象去接收查询结果。

Spring Data JPA 从入门到精通~查询结果的处理相关推荐

  1. Spring Data JPA 从入门到精通~查询方法的创建

    查询方法的创建 内部基础架构中有个根据方法名的查询生成器机制,对于在存储库的实体上构建约束查询很有用,该机制方法的前缀 find-By.read-By.query-By.count-By 和 get- ...

  2. Spring Data JPA 从入门到精通~@Version处理乐观锁的问题

    @Version 处理乐观锁的问题 @Version 乐观锁介绍 我们在研究 Auditing 的时候,发现了一个有趣的注解 @Version,源码如下: package org.springfram ...

  3. spring data jpa从入门到精通_Spring Data JPA的简单入门

    前言 spring data JPA是spring团队打造的sping生态全家桶的一部分,本身内核使用的是hibernate核心源码,用来作为了解java持久层框架基本构成的样本是再好不过的选择.最近 ...

  4. Spring Data JPA 从入门到精通~Naming命名策略详解及其实践

    Naming 命名策略详解及其实践 用 JPA 离不开 @Entity 实体,我都知道实体里面有字段映射,而字段映射的方法有两种: 显式命名:在映射配置时,设置的数据库表名.列名等,就是进行显式命名, ...

  5. Spring Data JPA 从入门到精通~Auditing及其事件详解

    Auditing 及其事件详解 Auditing 翻译过来是审计和审核,Spring 的优秀之处在于帮我们想到了很多繁琐事情的解决方案,我们在实际的业务系统中,针对一张表的操作大部分是需要记录谁什么时 ...

  6. Spring Data JPA 从入门到精通~实际工作的应用场景

    在实际工作中,有哪些场景会用到自定义 Repository 呢,这里列出几种实际在工作中的应用案例. 1. 逻辑删除场景 可以用到上面说的两种实现方式,如果有框架级别的全局自定义 Respositor ...

  7. Spring Data JPA 从入门到精通~自定义实现Repository

    EntityManager 的获取方式 我们既然要自定义,首先讲一下 EntityManager 的两种获取方式. 1. 通过 @PersistenceContext 注解. 通过将 @Persist ...

  8. Spring Data JPA 从入门到精通~EntityManager介绍

    EntityManager 介绍 我们前面已经无数次提到了,JPA 的默认 Repository 的实现类是 SimpleJpaRepository,而里面的具体实现就是调用的 EntityManag ...

  9. Spring Data JPA 从入门到精通~JpaSpecificationExecutor示例

    新建两个实体 @Entity(name = "UserInfoEntity") @Table(name = "user_info", schema = &quo ...

最新文章

  1. 获取生产订单的系统状态
  2. webservice ssl 2 下载webservice服务端所有的证书
  3. SAP CRM WebClient UI上UI标签文本的显示逻辑
  4. c 语言 宏 默认参数,C/C++预处理宏可以使用默认参数值吗?
  5. notepad++ 技巧
  6. php之类重写父类的构造方法,php实现parent调用父类的构造方法与被覆写的方法
  7. CentOS7下安装 mysql5.7.25(glibc版)(可用)
  8. java protobuffer 网络_C#与Java通过protobuf进行网络通信过程中遇到的问题
  9. 使用select模型详细介绍并使用此模型来监视标准输入缓冲区stdin里是否还有数据
  10. 网吧破解还原卡的方法总结!!
  11. Linux中tar命令用法
  12. 探秘广场舞:中老年市场绝佳切口?
  13. 深度解析反思型Essay怎么写?
  14. 爬虫项目十八:用Python对拉钩网全部城市所有招聘信息爬取
  15. Python数据分析U3-matplotlib可视化基础
  16. python plot如何保存图片_Matplotlib 保存图片、图画接口和显示中文的使用方法
  17. the little scheme Y-combinator
  18. 2020年上半年教育舆情新闻热点事件案例分析报告合集
  19. Lecture 12: Iterated Expectations; Sum of a Random Number of Random Variables
  20. SAP行业未来的发展如何,和chatGPT聊聊SAP行业的发展前景

热门文章

  1. 基本功 | Litho的使用及原理剖析
  2. 第四届泰迪杯数据挖掘大赛
  3. Githug第42关rebase_onto通关秘籍
  4. JavaScript每日学习日记(2)
  5. collection 多态 会自动转型为子类 继承多态需要显示转型
  6. C语言 - strcpy和strncpy的编程实现及总结
  7. WebService初入
  8. redis 类型、方法
  9. 暑训day1解题报告
  10. ios实例开发精品文章推荐(8.5)