|
此版本仍在开发中,尚未被视为稳定版本。如需最新稳定版本,请使用 Spring Data Cassandra 5.0.4! |
定义 Repository 接口
要定义一个仓库接口,首先需要定义一个特定于领域类的仓库接口。
该接口必须继承 Repository,并指定领域类和 ID 类型作为泛型参数。
如果你想为该领域类型暴露 CRUD 方法,可以改为继承 CrudRepository 或其某个变体,而不是直接继承 Repository。
精细调整存储库定义
有几种方式可以开始使用您的仓库接口。
通常的做法是继承 CrudRepository,它为你提供了用于 CRUD 功能的方法。
CRUD 代表创建(Create)、读取(Read)、更新(Update)和删除(Delete)。
从 3.0 版本开始,我们还引入了 ListCrudRepository,它与 CrudRepository 非常相似,但针对那些返回多个实体的方法,它返回的是 List 而不是 Iterable,你可能会觉得这样更易于使用。
如果您使用的是响应式存储库,可以根据所使用的响应式框架选择 ReactiveCrudRepository 或 RxJava3CrudRepository。
如果你使用的是 Kotlin,可以选择利用 Kotlin 协程的 CoroutineCrudRepository。
此外,如果你需要能够指定 PagingAndSortingRepository 抽象(或在第一种情况下指定 ReactiveSortingRepository 抽象)的方法,你可以扩展 RxJava3SortingRepository、CoroutineSortingRepository、Sort 或 Pageable。
请注意,从 Spring Data 3.0 版本开始,各种排序仓库接口不再像之前那样继承各自对应的 CRUD 仓库接口。
因此,如果你需要同时使用这两种功能,就必须同时继承这两个接口。
如果你不想继承 Spring Data 接口,也可以在你的仓库接口上使用 @RepositoryDefinition 注解。
继承某个 CRUD 仓库接口会暴露一整套用于操作实体的方法。
如果你希望有选择地暴露某些方法,可以从 CRUD 仓库中将你想要暴露的方法复制到你的领域仓库中。
这样做时,你可以更改这些方法的返回类型。
Spring Data 会在可能的情况下尊重你指定的返回类型。
例如,对于返回多个实体的方法,你可以选择 Iterable<T>、List<T>、Collection<T> 或 VAVR 列表。
如果你的应用程序中有多个仓库需要拥有相同的一组方法,你可以定义自己的基接口供它们继承。
此类接口必须使用 @NoRepositoryBean 注解。
这样可以防止 Spring Data 尝试直接创建该接口的实例而失败,因为此时仍包含泛型类型变量,Spring Data 无法确定该仓库对应的实体。
以下示例展示了如何有选择地暴露 CRUD 方法(在本例中为 findById 和 save):
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends Repository<T, ID> {
Optional<T> findById(ID id);
<S extends T> S save(S entity);
}
interface UserRepository extends MyBaseRepository<User, Long> {
User findByEmailAddress(EmailAddress emailAddress);
}
在前面的示例中,您为所有领域仓库定义了一个通用的基础接口,并暴露了 findById(…) 和 save(…) 方法。这些方法会被路由到 Spring Data 所提供的、您所选存储技术对应的基础仓库实现中(例如,如果您使用 JPA,则实现类为 SimpleJpaRepository),因为它们与 CrudRepository 中的方法签名相匹配。
因此,UserRepository 现在可以保存用户、通过 ID 查找单个用户,并触发查询以通过电子邮件地址查找 Users。
中间的仓库接口使用 @NoRepositoryBean 注解进行标注。
请确保将该注解添加到所有 Spring Data 在运行时不应创建实例的仓库接口上。 |
使用多个 Spring Data 模块的存储库
在应用程序中使用唯一的 Spring Data 模块会使事情变得简单,因为定义范围内的所有仓库接口都会绑定到该 Spring Data 模块。 有时,应用程序需要使用多个 Spring Data 模块。 在这种情况下,仓库定义必须区分不同的持久化技术。 当 Spring Data 在类路径上检测到多个仓库工厂时,会进入严格的仓库配置模式。 严格配置会利用仓库或领域类的详细信息,来决定仓库定义应绑定到哪个 Spring Data 模块:
-
如果仓库定义扩展了特定模块的仓库,则它是该特定 Spring Data 模块的有效候选者。
-
如果领域类使用了特定于模块的类型注解进行标注,那么它就是对应 Spring Data 模块的有效候选。 Spring Data 模块既接受第三方注解(例如 JPA 的
@Entity),也提供自己的注解(例如用于 Spring Data MongoDB 和 Spring Data Elasticsearch 的@Document)。
以下示例展示了一个使用模块特定接口(本例中为 JPA)的仓库:
interface MyRepository extends JpaRepository<User, Long> { }
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends JpaRepository<T, ID> { … }
interface UserRepository extends MyBaseRepository<User, Long> { … }
MyRepository 和 UserRepository 在其类型层次结构中继承了 JpaRepository。
它们是 Spring Data JPA 模块的有效候选者。
以下示例展示了一个使用泛型接口的仓库:
interface AmbiguousRepository extends Repository<User, Long> { … }
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends CrudRepository<T, ID> { … }
interface AmbiguousUserRepository extends MyBaseRepository<User, Long> { … }
AmbiguousRepository 和 AmbiguousUserRepository 在其类型层次结构中仅继承了 Repository 和 CrudRepository。
虽然在使用唯一的 Spring Data 模块时这样做没有问题,但在存在多个模块的情况下,无法区分这些仓库应绑定到哪个特定的 Spring Data 模块。
以下示例展示了一个使用带注解的领域类的仓库:
interface PersonRepository extends Repository<Person, Long> { … }
@Entity
class Person { … }
interface UserRepository extends Repository<User, Long> { … }
@Document
class User { … }
PersonRepository 引用了带有 JPA Person 注解的 @Entity,因此该仓库显然属于 Spring Data JPA。UserRepository 引用了带有 Spring Data MongoDB 的 User 注解的 @Document。
以下不良示例展示了一个使用带有混合注解的领域类的仓库:
interface JpaPersonRepository extends Repository<Person, Long> { … }
interface MongoDBPersonRepository extends Repository<Person, Long> { … }
@Entity
@Document
class Person { … }
此示例展示了一个同时使用 JPA 和 Spring Data MongoDB 注解的领域类。
它定义了两个仓库:JpaPersonRepository 和 MongoDBPersonRepository。
其中一个用于 JPA,另一个用于 MongoDB。
Spring Data 无法再区分这两个仓库,从而导致未定义的行为。
仓库类型详情 和 区分领域类注解 用于严格的仓库配置,以识别特定 Spring Data 模块的仓库候选者。 在同一领域类型上使用多种持久化技术特定的注解是可行的,并能实现领域类型在多种持久化技术之间的复用。 然而,Spring Data 将无法再确定一个唯一的模块来绑定该仓库。
区分仓库的最后一种方式是通过限定仓库基础包的范围。 基础包定义了扫描仓库接口定义的起始点,这意味着仓库定义必须位于相应的包中。 默认情况下,基于注解的配置会使用配置类所在的包。 在基于 XML 的配置中,基础包是必需的。
以下示例展示了基于注解驱动的基础包配置:
@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
class Configuration { … }