宣布适用于 Spring Data 3.0 的 ListCrudRepository 和相关功能

工程 | Jens Schauder | 2022 年 2 月 22 日 | ...

Spring Data 的 CrudRepository 拥有多种方法,这些方法返回由 repository 管理的实体实例。它使用 Iterable 而不是 List 来实现,这可能与预期不同。在许多情况下,这并不重要,因为你通常无论如何都需要遍历结果。但是,你偶尔可能会更喜欢 List。在这种情况下,Iterable 会令人感到不便。

我将更多地阐述最初为何做出这种选择,以及在你使用 Spring Data 2.x 时如何处理。然而,让我先公布好消息

返回 List 的 Repositories

Spring Data 3.0.0 在最新的快照版本中提供了 ListCrudRepository,它在 CrudRepository 返回 Iterable 的地方返回 List

示例 1. CrudRepository 对比 ListCrudRepository

@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {

	<S extends T> S save(S entity);

	<S extends T> Iterable<S> saveAll(Iterable<S> entities);

	Optional<T> findById(ID id);

	boolean existsById(ID id);

	Iterable<T> findAll();

	Iterable<T> findAllById(Iterable<ID> ids);

	long count();

	void deleteById(ID id);

	void delete(T entity);

	void deleteAllById(Iterable<? extends ID> ids);

	void deleteAll(Iterable<? extends T> entities);

	void deleteAll();
}

@NoRepositoryBean
public interface ListCrudRepository<T, ID> extends CrudRepository<T, ID> {

	<S extends T> List<S> saveAll(Iterable<S> entities);

	List<T> findAll();

	List<T> findAllById(Iterable<ID> ids);
}

拆分排序 Repositories

常用的 PagingAndSortingRepository 过去继承自 CrudRepository,但现在不再如此。这允许你将其与 CrudRepositoryListCrudRepository 或你自己创建的基础接口结合使用。这意味着,即使你已经继承自 PagingAndSortingRepository,现在也必须显式地继承一个 CRUD 片段。

示例 2. 分页和排序 repository — 2.x 版本

public interface PersonRepository<Person, Long> extends PagingAndSortingRepository<Person, Long> {}

示例 3. 分页和排序 repository — 3.x 版本

public interface PersonRepository<Person, Long> extends PagingAndSortingRepository<Person, Long>, ListCrudRepository<Person, Long> {}

还有其他返回 Iterable<T> 的接口,它们现在也有了一个返回 List<T> 的配套接口。

返回 Iterable 的片段接口

返回 List 的新片段接口

QuerydslPredicateExecutor

ListQuerydslPredicateExecutor

QueryByExampleExecutor

ListQueryByExampleExecutor

此外,与 PagingAndSortingRepository 类似,其他排序 repository 接口过去继承各自的 CRUD 变体,但现在不再如此。

排序片段接口

不再继承的 CRUD repository

ReactiveSortingRepository

ReactiveCrudRepository

CoroutineSortingRepository

CoroutineCrudRepository

Rx3JavaSortingRepository

Rx3JavaCrudRepository

这意味着,当你使用这些接口中的任何一个作为 Spring Data repositories 的基础时,现在需要额外继承相应的 CRUD repository(假设你确实对使用它的 CRUD 功能感兴趣)。

2.x 版本怎么办?

如果你还没有准备好尝试 3.0.0 快照版本,你仍然有所有现有的选项来避免处理返回值为 Iterable 的情况。

  1. 你不需要继承 CrudRepository。你可以改用 Repository,它没有任何方法。现在你只需添加你实际想要的方法,并使用你想要的返回类型。如果它们与 CrudRepository 中的方法匹配,但返回 CollectionList 而不是 Iterable,Spring Data 会为你处理转换。此外,在大多数情况下,你可能在生产环境中并不真正需要 deleteAll,对吧?

  2. 如果你想要 CrudRepository 的所有方法,但希望用其他类型替换 Iterable,你可以通过继承 CrudRepository 并重写你想要更改的方法来实现。

  3. 你可以在自己的基础 repository 接口中实现,而不是在每个声明的 repository 接口中都这样做。对于这种方法,使用与上面相同的方法,但用 @NoRepositoryBean 进行注解,这样 Spring Data 就不会尝试为其创建实现。你的实际 repository 接口现在可以继承此接口。流行的 JpaRepository 就是这样一个接口。

  4. 与上面类似,你可能使用 Streamable 作为返回类型,它是一个 Iterable,但提供了直接转换为 StreamListSet 的方法。

  5. 如果你不想更改 repositories,可以使用 StreamableIterable 转换为 StreamListSet 接口。

最初为何选择 Iterable?

  1. CrudRepository 的方法需要由每个 Spring Data 实现来提供。因此,它不仅是 Spring Data 用户的 API,也是提供 Spring Data 模块的厂商的 SPI。此外,这样的模块可能不希望在返回之前填充一个完整的列表,而是快速返回一个 Iterable,同时仍在加载和处理数据。

  2. 如果 CrudRepository 返回 List,你就无法重写其方法以返回 StreamableSetCollectionIterable

  3. Streamable 本来会是一个很棒的返回类型,因为它兼具灵活性和易用性。不幸的是,它会迫使你在领域模型中使用一个 Spring 接口,许多人认为这是不可接受的,或者至少是一种代码异味(code smell)。

此外,一旦 Iterable 已经存在,在不破坏现有代码的情况下更改 API 就变得困难甚至不可能。因此,找到一个既提高了易用性,又限制了变更造成的破坏的解决方案花费了一些时间。

我们希望你喜欢这个解决方案,并且我们相信你会将你的想法告诉我们。

获取 Spring 时事通讯

订阅 Spring 时事通讯,保持联系

订阅

领先一步

VMware 提供培训和认证,助你加速进步。

了解更多

获取支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,仅需一个简单的订阅。

了解更多

近期活动

查看 Spring 社区的所有近期活动。

查看全部