领先一步
VMware 提供培训和认证,助您加速进步。
了解更多距离我们发布 Spring Data JPA 1.0 GA 仅仅几天时间,这是 Spring Data 项目的第一个主版本,其中包含了我们在 Spring Data Commons 模块中的仓库抽象的实现。仓库抽象包含三个主要部分:定义仓库接口、暴露 CRUD 方法和添加查询方法。在第一篇 Spring Data JPA 博客文章中已详细讨论了添加查询方法。但是,定义仓库接口和暴露 CRUD 方法在之前的博客文章中引发了一些问题。因此,我们将立即仔细研究它们,并讨论用户可用的选项。
仓库接口的声明可以通过两种方式完成:使用注解或通过扩展标记接口。
@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 方法。为了方便起见,我们提供了两个单独的接口:CrudRepository 和 PagingAndSortingRepository,后者扩展了前者。顾名思义,CrudRepository 暴露了基本的 CRUD 方法,例如 T save(T entity)、T findOne(ID id) 和 void delete(T entity)。PagingAndSortingRepository 暴露了开箱即用的分页方法,例如 Page。要将这些方法暴露给您的客户端,只需像这样扩展这些接口即可:
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 来注解接口,以防止 Spring Data 类路径扫描拾取带有正式类型参数 T 和 ID 的接口。最后——也是可能是最关键的——部分是方法签名必须与 CrudRepository 或 PagingAndSortingRepository 中的签名匹配。您的实际仓库接口将如下所示:
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> { … }
这假设 Customer 和 Person 共享一个 name 属性(不一定是继承关系)。因为查询将自动从方法名派生。通过简单地声明 JPA 命名查询 Customer.findByName 和 Person.findByName,甚至可以手动定义为这两个具体仓库执行的查询。使用这种方法,您可以轻松地为您的应用程序场景创建量身定制的基接口。
这就给我们留下了何时使用特定技术基接口(如 JpaRepository)的问题。第一个原因可能是快速入门:它暴露了基于 List 的读取方法(例如 List),而使用 JPA 的开发人员可能更习惯这些方法,以及所有从其超接口派生的 CRUD 方法。我们还在这里暴露了一些 JPA 特定的方法,因为某些客户端可能需要调用它们。我们实际上建议不要向客户端暴露特定技术,但有时实用主义胜于理论。所有这些特殊性也可以通过自定义基接口来暴露(重新声明返回 List 的读取方法,暴露 JPA 特定的方法),因此您几乎可以选择在特定情况下所需的最小工作量。
因此,如果您想开始使用 Spring Data 的仓库抽象,请随时在 Github 上查看我们为 JPA 和 Mongo 提供的示例项目。