宣布 Spring Data 3.0 的 ListCrudRepository 及相关功能

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

Spring Data 的CrudRepository有各种方法可以返回由仓库管理的多个实体实例。它通过使用Iterable而不是 List 来实现,这可能出乎意料。在许多情况下,这没有关系,因为您通常无论如何都想遍历结果。但是,您偶尔可能更喜欢 List。在这种情况下,Iterable 会很烦人。

我将更多地讨论当初为何做出这种选择,以及在 Spring Data 2.x 上如何处理它。不过,让我先公布好消息

返回列表的仓库

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);
}

拆分排序存储库

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

示例 2. 分页和排序存储库 — 2.x 版本

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

示例 3. 分页和排序存储库 — 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 类似,其他排序存储库接口过去也继承其各自的 CRUD 变体,但现在不再如此。

排序片段接口

不再继承的 CRUD 存储库

ReactiveSortingRepository

ReactiveCrudRepository

CoroutineSortingRepository

CoroutineCrudRepository

Rx3JavaSortingRepository

Rx3JavaCrudRepository

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

2.x 版本呢?

如果你还没有准备好使用 3.0.0 快照版本,你仍然可以使用所有 现有选项 来避免处理作为返回值的 Iterable

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

  2. 如果你想要 CrudRepository 的所有方法,但希望 Iterable 被其他类型替换,你可以通过扩展 CrudRepository 并覆盖你想要更改的方法来实现。

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

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

  5. 如果你不想修改存储库,你可以使用 StreamableIterable 进行转换StreamListSet 的接口。

为什么最初是 Iterable?

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

  2. 如果 CrudRepository 返回 List,你将无法覆盖其方法以返回 StreamableSetCollectionIterable

  3. Streamable 本可以是一个很棒的返回类型,因为它将灵活性与可用性结合在一起。不幸的是,它会强制你的领域模型中包含一个 Spring 接口,这在许多人看来是不可接受的,或者至少是一种代码异味。

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

我们希望你喜欢这个解决方案,并且我们相信你会让我们知道你的想法。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

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

了解更多

获得支持

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

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有