1. 概述
Spring Data 现已支持 Java 8 的核心特性——包括 Optional
、Stream API
和 CompletableFuture
。
本文将通过几个示例,展示如何在 Spring Data 框架中使用这些特性。
2. Optional
的使用
先看 CRUD 仓库方法——现在它们会返回 Optional
包装的结果:
public interface CrudRepository<T, ID> extends Repository<T, ID> {
Optional<T> findById(ID id);
}
返回 Optional
实例是个明确的信号:查询结果可能不存在。关于 Optional
的更多细节可参考这里。
我们只需将返回类型声明为 Optional
即可:
public interface UserRepository extends JpaRepository<User, Integer> {
Optional<User> findOneByName(String name);
}
3. Stream
API 集成
Spring Data 还支持 Java 8 最重要的特性之一——Stream API
。
过去,当需要返回多条记录时,我们只能返回集合:
public interface UserRepository extends JpaRepository<User, Integer> {
// ...
List<User> findAll();
// ...
}
这种实现有个明显问题:内存消耗大。所有查询结果必须立即加载并保存在内存中。
改进方案是使用分页:
public interface UserRepository extends JpaRepository<User, Integer> {
// ...
Page<User> findAll(Pageable pageable);
// ...
}
某些场景下分页足够,但数据量巨大时,频繁分页请求反而会成为性能瓶颈。
得益于 Java 8 的 Stream API
和 JPA 提供程序——现在我们可以直接让仓库方法返回对象流:
public interface UserRepository extends JpaRepository<User, Integer> {
// ...
Stream<User> findAllByName(String name);
// ...
}
Spring Data 会利用提供程序特定实现(如 Hibernate 的 ScrollableResultSet
或 EclipseLink 的 ScrollableCursor
)流式传输结果。这显著降低了内存消耗和数据库查询次数,性能远超前两种方案。
⚠️ 使用 Stream
处理数据后必须关闭流,可通过以下两种方式实现:
- 调用
close()
方法 - 使用
try-with-resources
语法:
try (Stream<User> foundUsersStream
= userRepository.findAllByName("张三")) {
assertThat(foundUsersStream.count(), equalTo(3L));
}
❌ 务必在事务中调用仓库方法,否则会抛出异常:
org.springframework.dao.InvalidDataAccessApiUsageException
: 您试图在没有保持连接打开的事务中执行流式查询方法,导致Stream
无法被消费。请确保消费流的代码使用@Transactional
或其他方式声明(只读)事务。
4. CompletableFuture
异步支持
Spring Data 仓库可通过 Java 8 的 CompletableFuture
和 Spring 异步执行机制实现异步操作:
@Async
CompletableFuture<User> findOneByStatus(Integer status);
客户端调用此方法会立即返回 Future
对象,而实际方法执行将在另一个线程中完成。
关于 CompletableFuture
的更多细节可参考这里。
5. 总结
本文展示了 Java 8 特性如何与 Spring Data 协同工作。完整示例代码可在 GitHub 获取。