领先一步
VMware 提供培训和认证,助你加速进步。
了解更多Spring Data 的 CrudRepository
拥有多种方法,这些方法返回由 repository 管理的实体实例。它使用 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
,但现在不再如此。这允许你将其与 CrudRepository
或 ListCrudRepository
或你自己创建的基础接口结合使用。这意味着,即使你已经继承自 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
CoroutineSortingRepository
CoroutineCrudRepository
Rx3JavaSortingRepository
这意味着,当你使用这些接口中的任何一个作为 Spring Data repositories 的基础时,现在需要额外继承相应的 CRUD repository(假设你确实对使用它的 CRUD 功能感兴趣)。
如果你还没有准备好尝试 3.0.0 快照版本,你仍然有所有现有的选项来避免处理返回值为 Iterable
的情况。
你不需要继承 CrudRepository
。你可以改用 Repository
,它没有任何方法。现在你只需添加你实际想要的方法,并使用你想要的返回类型。如果它们与 CrudRepository
中的方法匹配,但返回 Collection
或 List
而不是 Iterable
,Spring Data 会为你处理转换。此外,在大多数情况下,你可能在生产环境中并不真正需要 deleteAll
,对吧?
如果你想要 CrudRepository
的所有方法,但希望用其他类型替换 Iterable
,你可以通过继承 CrudRepository
并重写你想要更改的方法来实现。
你可以在自己的基础 repository 接口中实现,而不是在每个声明的 repository 接口中都这样做。对于这种方法,使用与上面相同的方法,但用 @NoRepositoryBean
进行注解,这样 Spring Data 就不会尝试为其创建实现。你的实际 repository 接口现在可以继承此接口。流行的 JpaRepository
就是这样一个接口。
与上面类似,你可能使用 Streamable
作为返回类型,它是一个 Iterable
,但提供了直接转换为 Stream
、List
和 Set
的方法。
如果你不想更改 repositories,可以使用 Streamable
将 Iterable
转换为 Stream
、List
或 Set
接口。
CrudRepository
的方法需要由每个 Spring Data 实现来提供。因此,它不仅是 Spring Data 用户的 API,也是提供 Spring Data 模块的厂商的 SPI。此外,这样的模块可能不希望在返回之前填充一个完整的列表,而是快速返回一个 Iterable
,同时仍在加载和处理数据。
如果 CrudRepository
返回 List
,你就无法重写其方法以返回 Streamable
、Set
、Collection
或 Iterable
。
Streamable
本来会是一个很棒的返回类型,因为它兼具灵活性和易用性。不幸的是,它会迫使你在领域模型中使用一个 Spring 接口,许多人认为这是不可接受的,或者至少是一种代码异味(code smell)。
此外,一旦 Iterable
已经存在,在不破坏现有代码的情况下更改 API 就变得困难甚至不可能。因此,找到一个既提高了易用性,又限制了变更造成的破坏的解决方案花费了一些时间。
我们希望你喜欢这个解决方案,并且我们相信你会将你的想法告诉我们。