🎉🎉《Spring Boot实战案例合集》目前已更新187个案例,我们将持续不断的更新。文末有电子书目录。
💪💪永久更新承诺
我们郑重承诺,所有订阅合集的粉丝都将享受永久免费的后续更新服务。
💌💌如何获取
订阅我们的合集《点我订阅》,并通过私信联系我们,我们将第一时间将电子书发送给您。
环境:SpringBoot3.4.2
1. 简介
Spring Data JPA 是 Spring 框架中用于简化 JPA(Java Persistence API)操作的模块,旨在提升数据库访问效率并减少样板代码。通过提供统一的数据访问接口(如 JpaRepository)和自动方法名解析,开发者无需手动编写 SQL 或实现复杂查询。其核心优点包括:降低开发成本,通过约定优于配置快速构建 CRUD 操作;支持自定义查询方法、分页排序等高级功能,显著提升开发效率与代码可维护性。
在本篇文章中,我们将介绍 Spring Data JPA 所具备的 6 个强大功能,这些功能在提升数据库操作效率、性能、简化开发流程等方面发挥着关键作用。
准备环境
创建如下Book实体
public class Book {private Long id ;private String title ;private String isbn ;private String description ;private Integer page ;private BigDecimal price ;private String rating ;}
准备如下数据
2.2 支持Optional返回值
Java 8 或 Guava 的 Optional。期望查询方法最多返回一个结果。如果未找到结果,则返回 Optional.empty() 或 Optional.absent()。如果找到多个结果,则触发 IncorrectResultSizeDataAccessException 异常。如下示例:
public interface BookRepository extends JpaRepository<Book, Long> {Optional<Book> findByTitle(String title) ;}
通过返回 Optional 避免空指针,显式表达可能无值,强制判空处理,提升代码安全性与可读性。
2.3 Stream流式API
过去,每当我们需要返回多个结果时,就必须返回一个集合,如下示例:
List<Book> findAll() ;
这种实现方式的一个问题是内存消耗。我们不得不提前加载所有检索到的对象,并将其保留在内存中。不过这不是问题我们可以通过分页来改进,如下示例:
Page<Book> findAll(Pageable pageable) ;
在绝大多数场景下,这样做已经足够,但在其他场景中——由于查询数据所需的请求次数过多,分页则不是理想之选。
现在我们可以直接返回Java 8的Stream对象,如下示例:
Stream<Book> findByPriceGreaterThan(BigDecimal price);
该示例查询大于给定价格的所有图书。
接下来,我们调用上面的方法:
private BookRepository bookRepository ;public void testStream() {this.bookRepository.findByPriceGreaterThan(BigDecimal.ZERO).forEach(System.out::println) ;}
运行结果
意思是:你的Stream流的操作必须在一个事务当中。
修改代码如下:
public void testStream() {// ...}
控制台输出
推荐做法:
public void testStream() {try (Stream<Book> stream = this.bookRepository.findByPriceGreaterThan(BigDecimal.ZERO)) {stream.forEach(System.out::println) ;}}
2.4 返回CompletableFuture + @Async
我们还可以将返回值设置为CompletableFuture同时将方法设置为异步线程执行。如下示例:
CompletableFuture<Book> findByIsbn(String isbn) ;
调用此方法的客户端会立即返回一个 Future 对象,但该方法会继续在另一个线程中执行。
异步查询提升响应速度,避免阻塞主线程,适合高延迟操作,提高系统并发处理能力。
2.5 滚动查询(使用偏移量)
滚动API提供了以分块方式遍历大量结果的功能。它提供了稳定的排序、滚动类型以及结果限制功能。我们可以使用属性名称来定义简单的排序表达式,并通过查询派生使用Top或First来定义静态结果限制。如下示例:
Window<Book> findFirst5ByRatingOrderById(String rating, OffsetScrollPosition position);
按指定评分查询书籍,每次返回最多5条,按ID排序。
运行上面的方法:
public void testScrollWindow() {String rating = "1" ;OffsetScrollPosition offset = ScrollPosition.offset();Window<Book> books = bookRepository.findFirst5ByRatingOrderById(rating, offset);do {books.forEach(System.out::println) ;System.err.println("------------------------------------") ;books = bookRepository.findFirst5ByRatingOrderById(rating, (OffsetScrollPosition) books.positionAt(books.size() - 1));} while (!books.isEmpty() && books.hasNext());}
控制台输出结果
自动通过LIMIT分页查询数据。
2.6 滚动查询(使用Keyset过滤)
基于偏移量的分页要求大多数数据库在服务器返回结果之前先实例化整个结果集。因此,虽然客户端只能看到请求结果的一部分,但服务器需要构建完整的结果集,这会导致额外的负载。
键集过滤(Keyset-Filtering)通过利用数据库的内置功能来检索结果子集,旨在减少单个查询的计算和I/O需求。这种方法通过将键传递到查询中来保持一组键,以便恢复滚动操作,从而有效地修改筛选条件。
Window<Book> findFirst5ByRatingOrderById(String rating, KeysetScrollPosition position);
运行上面的方法:
public void testScrollWindow() {String rating = "1" ;WindowIterator<Book> books = WindowIterator.of(position -> bookRepository.findFirst5ByRatingOrderById(rating, (KeysetScrollPosition) position)).startingAt(ScrollPosition.keyset()) ;books.forEachRemaining(System.out::println) ;}
控制台输出结果
键集过滤通过记录最后一条记录的键(如id),后续查询基于此键筛选,避免全量扫描,减少计算与IO开销。相比偏移量方式需全量排序定位,键集过滤性能更优,尤其适合大数据量分页,且能稳定排序,保证结果一致性。
性能相差60倍!Spring Boot 批量插入你做对了吗?5种方式对比
Spring Boot API接口性能提升:10项核心优化技能全解析
技术专家!Spring AOP + Nacos + Prometheus 实现动态限流及实时监控
30+个 CompletableFuture 高频场景案例,让你的系统性能飙升300%
Jackson 5 大逆天神技,搞定双向关联输出 JSON 问题
真心强大!Spring AI + MCP 智能体工具动态更新
Jackson!JSON处理的瑞士军刀ObjectMapper
强大!Spring Boot 巧妙利用 SpEL 实现复杂的规则运算


