更进一步
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
注释该接口,以防止具有形式类型参数 T
和 ID
的接口被 Spring Data 类路径扫描拾取。最后一个 - 也可能是最关键的部分 - 是方法签名必须与 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 的示例项目。