微调 Spring Data 仓库

工程 | Oliver Drotbohm | 2011年7月27日 | ...

距离我们发布 Spring Data JPA 1.0 GA 仅仅几天时间,这是 Spring Data 项目的第一个主要版本,它在我们的 Spring Data Commons 模块中附带了存储库抽象的实现。存储库抽象包含三个主要部分:定义存储库接口、公开 CRUD 方法以及添加查询方法。在 第一篇 Spring Data JPA 博客文章 中详细讨论了添加查询方法。但是,定义存储库接口和公开 CRUD 方法在之前的博客文章中引发了一些问题。因此,我们现在将仔细研究它们,讨论用户可用的选项。

声明 Spring Data 存储库接口

可以通过两种方式声明存储库接口:使用注解或扩展标记接口


@RepositoryDefinition(domainClass=Customer.class, idClass=Long.class)
public interface CustomerRepository {
  // declare query methods
}

public interface AccountRepository extends Repository<Account, Long> {
  // declare query methods
}

选择哪种风格主要取决于个人喜好。希望尽可能减少代码依赖性的纯粹主义者可能会坚持使用基于注解的方法,但基于继承的方法实际上与其余的编程模型更加一致。无论您选择哪种方法,Spring Data 基础设施都将了解如何实现这些接口中定义的存储库方法。

CRUD 方法

您可能要做的下一步是公开 CRUD 方法。为了帮助做到这一点,我们提供了两个单独的接口:CrudRepositoryPagingAndSortingRepository,后者扩展了前者。顾名思义,CrudRepository 公开了基本的 CRUD 方法,例如 T save(T entity)T findOne(ID id)void delete(T entity)PagingAndSortingRepository 公开了开箱即用的分页方法,例如 Page findAll(Pageable pageable)。要将这些方法公开给您的客户端,只需像这样扩展这些接口

public interface CustomerRepository extends PagingAndSortingRepository<Customer, Long> {
  // declare query methods here
}

微调存储库接口

扩展前面显示的接口对于快速入门非常简洁,但有一个明显的缺点:您向客户端公开了一组预定义的操作,而这些操作不受您的控制,到目前为止,基本上是全有或全无。似乎无法仅公开读取操作,同时完全隐藏状态更改操作。

为了解决这个问题,我们允许您为您的存储库定义自定义基础接口,您可以在其中选择性地公开您喜欢的 CRUD 方法


@NoRepositoryBean
public interface ReadOnlyRepository<T, ID extends Serializable> extends Repository<T, ID> {
  T findOne(ID id);
  Iterable<T> findAll();
  Iterable<T> findAll(Sort sort);
  Page<T> findAll(Pageable pageable);
}

这里需要考虑一些关键事项:首先,中间接口也必须是 Spring Data 存储库接口,这意味着它必须使用 @RepositoryDefinition 进行注解,或者如这里所示扩展 Repository。您还应该使用 @NoRepositoryBean 注解该接口,以防止具有形式类型参数 TID 的接口被 Spring Data 类路径扫描选中。最后(可能是最关键的)部分是方法签名必须与 CrudRepositoryPagingAndSortingRepository 中的方法签名匹配。然后,您的实际存储库接口将如下所示

public interface CustomerRepository extends ReadOnlyRepository<Customer, Long> {
  // declare query methods here
}

由于支持为该接口创建的代理的存储库实现实现了您在自定义中间接口中声明的方法,因此我们可以将对这些方法的调用路由到实现中。您甚至可以在共享基础接口中声明共享查询方法

@NoRepositoryBean
public interface NamedRepository<T, ID extends Serializable> extends ReadOnlyRepository<T, ID> {
  List<T> findByName(String name);
}

public interface CustomerRepository extends NamedRepository<Customer, Long> { … }

public interface PersonRepository extends NamedRepository<Person, Long> { … }

这假设 CustomerPerson 共享一个 name 属性(不一定通过继承)。因为查询将自动从方法名称派生出来。甚至可以通过简单地声明 JPA 命名查询 Customer.findByNamePerson.findByName 来手动定义对这两个具体存储库执行的查询。使用这种方法,您可以轻松地为您的应用程序场景设计定制的基础接口。

特定于技术的接口

这让我们面临一个问题,即何时使用特定于技术的基接口,例如 JpaRepository。第一个原因可能仅仅是快速入门:它公开了基于 List 的读取方法(例如 List findAll()),JPA 开发人员可能也更习惯使用这些方法,以及从其超接口派生的所有 CRUD 方法。我们还在此处公开了某些 JPA 特定的方法,因为某些客户端可能需要调用它们。我们实际上建议不要向客户端公开特定于技术的内容,但有时实用主义胜于理论。所有这些特性也可以通过自定义基础接口公开(重新声明返回 List 的读取方法,公开 JPA 特定的方法),因此您几乎可以选择在特定情况下最小的工作量。

因此,如果您想开始使用 Spring Data 的存储库抽象,请随时查看我们针对 JPAMongo 的示例项目(位于 Github 上)。

获取 Spring 新闻通讯

与 Spring 新闻通讯保持联系

订阅

走在前面

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

了解更多

获得支持

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

了解更多

即将举行的活动

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

查看全部