Custom Repository Implementations
Spring Data 提供了各种选项来创建几乎无需编码的查询方法。但是,当那些选项不符合您的需要时,您也可以为存储库方法提供您自己的自定义实现。本部分介绍如何执行此操作。
Spring Data provides various options to create query methods with little coding. But when those options don’t fit your needs you can also provide your own custom implementation for repository methods. This section describes how to do that.
Customizing Individual Repositories
要使用自定义功能丰富存储库,您必须首先按以下方式定义片段接口和自定义功能的实现:
To enrich a repository with custom functionality, you must first define a fragment interface and an implementation for the custom functionality, as follows:
interface CustomizedUserRepository {
void someCustomMethod(User user);
}
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {
public void someCustomMethod(User user) {
// Your custom implementation
}
}
类名中与片段接口相对应最重要的部分是 |
The most important part of the class name that corresponds to the fragment interface is the |
该实现本身不依赖于 Spring Data,并且可以是常规 Spring Bean。因此,可以使用标准依赖注入行为注入对其他 Bean(例如 JdbcTemplate
)的引用,可以使用切面等等。
The implementation itself does not depend on Spring Data and can be a regular Spring bean.
Consequently, you can use standard dependency injection behavior to inject references to other beans (such as a JdbcTemplate
), take part in aspects, and so on.
然后,您可以让您的存储库接口扩展片段接口,如下所示:
Then you can let your repository interface extend the fragment interface, as follows:
interface UserRepository extends CrudRepository<User, Long>, CustomizedUserRepository {
// Declare query methods here
}
用您的存储库接口扩展片段接口将 CRUD 和自定义功能组合在一起,并使其可供客户端使用。
Extending the fragment interface with your repository interface combines the CRUD and custom functionality and makes it available to clients.
使用形成存储库组合的片段来实现 Spring Data 存储库。片段是基础存储库、功能方面(例如 QueryDsl)以及自定义接口及其实现。每次将一个界面添加到您的存储库界面时,都会通过添加一个片段来增强组合。基础存储库和存储库方面实现由每个 Spring Data 模块提供。
Spring Data repositories are implemented by using fragments that form a repository composition. Fragments are the base repository, functional aspects (such as QueryDsl), and custom interfaces along with their implementations. Each time you add an interface to your repository interface, you enhance the composition by adding a fragment. The base repository and repository aspect implementations are provided by each Spring Data module.
以下示例显示了自定义接口及其实现:
The following example shows custom interfaces and their implementations:
interface HumanRepository {
void someHumanMethod(User user);
}
class HumanRepositoryImpl implements HumanRepository {
public void someHumanMethod(User user) {
// Your custom implementation
}
}
interface ContactRepository {
void someContactMethod(User user);
User anotherContactMethod(User user);
}
class ContactRepositoryImpl implements ContactRepository {
public void someContactMethod(User user) {
// Your custom implementation
}
public User anotherContactMethod(User user) {
// Your custom implementation
}
}
以下示例显示了扩展 CrudRepository
的自定义存储库的接口:
The following example shows the interface for a custom repository that extends CrudRepository
:
interface UserRepository extends CrudRepository<User, Long>, HumanRepository, ContactRepository {
// Declare query methods here
}
存储库可以由按声明顺序导入的多个自定义实现组成。自定义实现的优先级高于基础实现和存储库切面。这种排序方式可以覆盖基础存储库和切面方法,如果两个片段提供相同的函数签名,还可以解决模糊性。存储库片段不限于在单个存储库接口中使用。多个存储库可以使用片段接口,从而允许跨不同存储库重用自定义项。
Repositories may be composed of multiple custom implementations that are imported in the order of their declaration. Custom implementations have a higher priority than the base implementation and repository aspects. This ordering lets you override base repository and aspect methods and resolves ambiguity if two fragments contribute the same method signature. Repository fragments are not limited to use in a single repository interface. Multiple repositories may use a fragment interface, letting you reuse customizations across different repositories.
以下示例展示了存储库片段及其实现:
The following example shows a repository fragment and its implementation:
save(…)
interface CustomizedSave<T> {
<S extends T> S save(S entity);
}
class CustomizedSaveImpl<T> implements CustomizedSave<T> {
public <S extends T> S save(S entity) {
// Your custom implementation
}
}
以下示例展示了使用上述存储库片段的存储库:
The following example shows a repository that uses the preceding repository fragment:
interface UserRepository extends CrudRepository<User, Long>, CustomizedSave<User> {
}
interface PersonRepository extends CrudRepository<Person, Long>, CustomizedSave<Person> {
}
Configuration
存储库基础设施尝试通过扫描发现存储库的包下方的类,以自动检测自定义实现片段。这些类需要遵循命名规范,附加默认值为“Impl
”的后缀。
The repository infrastructure tries to autodetect custom implementation fragments by scanning for classes below the package in which it found a repository.
These classes need to follow the naming convention of appending a postfix defaulting to Impl
.
以下示例展示了使用默认后缀的存储库和设置后缀的自定义值的存储库:
The following example shows a repository that uses the default postfix and a repository that sets a custom value for the postfix:
- Java
-
@Enable{store}Repositories(repositoryImplementationPostfix = "MyPostfix") class Configuration { … }
- XML
-
<repositories base-package="com.acme.repository" /> <repositories base-package="com.acme.repository" repository-impl-postfix="MyPostfix" />
上一示例中的第一个配置尝试查找称为“com.acme.repository.CustomizedUserRepositoryImpl
”的类,以用作自定义存储库实现。第二个示例尝试查找“com.acme.repository.CustomizedUserRepositoryMyPostfix
”。
The first configuration in the preceding example tries to look up a class called com.acme.repository.CustomizedUserRepositoryImpl
to act as a custom repository implementation.
The second example tries to look up com.acme.repository.CustomizedUserRepositoryMyPostfix
.
Resolution of Ambiguity
如果在不同包中发现具有匹配类名的多个实现,Spring Data 会使用 Bean 名称来识别要使用哪一个。
If multiple implementations with matching class names are found in different packages, Spring Data uses the bean names to identify which one to use.
鉴于前面显示的“CustomizedUserRepository
”的两个自定义实现,使用了第一个实现。其 Bean 名称是“customizedUserRepositoryImpl
”,它匹配片段接口(“CustomizedUserRepository
”)加上后缀“Impl
”。
Given the following two custom implementations for the CustomizedUserRepository
shown earlier, the first implementation is used.
Its bean name is customizedUserRepositoryImpl
, which matches that of the fragment interface (CustomizedUserRepository
) plus the postfix Impl
.
package com.acme.impl.one;
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {
// Your custom implementation
}
package com.acme.impl.two;
@Component("specialCustomImpl")
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {
// Your custom implementation
}
如果你使用“@Component("specialCustom")
”对“UserRepository
”接口进行注释,则 Bean 名称加上“Impl
”匹配在“com.acme.impl.two
”中为存储库实现定义的名称,并且它将替代第一个名称。
If you annotate the UserRepository
interface with @Component("specialCustom")
, the bean name plus Impl
then matches the one defined for the repository implementation in com.acme.impl.two
, and it is used instead of the first one.
Manual Wiring
如果您的自定义实现仅使用基于注释的配置和自动装配,则前面所示的方法将很好地工作,因为它被视为任何其他 Spring bean。如果您的实现片段 bean 需要特殊连接,您可以声明该 bean 并根据 preceding section中描述的约定对其命名。然后,基础架构将按名称引用手动定义的 bean 定义,而不是自己创建 bean 定义。以下示例展示如何手动连接自定义实现:
If your custom implementation uses annotation-based configuration and autowiring only, the preceding approach shown works well, because it is treated as any other Spring bean. If your implementation fragment bean needs special wiring, you can declare the bean and name it according to the conventions described in the preceding section. The infrastructure then refers to the manually defined bean definition by name instead of creating one itself. The following example shows how to manually wire a custom implementation:
- Java
-
class MyClass { MyClass(@Qualifier("userRepositoryImpl") UserRepository userRepository) { … } }
- XML
-
<repositories base-package="com.acme.repository" /> <beans:bean id="userRepositoryImpl" class="…"> <!-- further configuration --> </beans:bean>
Customize the Base Repository
preceding section 中所述的方法需要在您想要自定义基本存储库行为时自定义每个存储库接口,以影响所有存储库。而要更改所有存储库的行为,您可以创建实现,以扩展特定的持久性技术存储库基础类。然后,此类充当存储库代理的自定义基础类,如以下示例所示:
The approach described in the preceding section requires customization of each repository interfaces when you want to customize the base repository behavior so that all repositories are affected. To instead change behavior for all repositories, you can create an implementation that extends the persistence technology-specific repository base class. This class then acts as a custom base class for the repository proxies, as shown in the following example:
class MyRepositoryImpl<T, ID>
extends SimpleJpaRepository<T, ID> {
private final EntityManager entityManager;
MyRepositoryImpl(JpaEntityInformation entityInformation,
EntityManager entityManager) {
super(entityInformation, entityManager);
// Keep the EntityManager around to used from the newly introduced methods.
this.entityManager = entityManager;
}
@Transactional
public <S extends T> S save(S entity) {
// implementation goes here
}
}
该类需要具有存储特定仓库工厂实现使用的超类的构造函数。如果仓库基本类有多个构造函数,请重写采用 EntityInformation
加上存储特定基础设施对象(例如 EntityManager
或模板类)的那个构造函数。
The class needs to have a constructor of the super class which the store-specific repository factory implementation uses.
If the repository base class has multiple constructors, override the one taking an EntityInformation
plus a store specific infrastructure object (such as an EntityManager
or a template class).
最后一步是让 Spring Data 基础设施注意自定义存储库基本类。在配置中,你可以使用“repositoryBaseClass
”来实现,如以下示例所示:
The final step is to make the Spring Data infrastructure aware of the customized repository base class.
In configuration, you can do so by using the repositoryBaseClass
, as shown in the following example:
- Java
-
@Configuration @Enable{store}Repositories(repositoryBaseClass = MyRepositoryImpl.class) class ApplicationConfiguration { … }
- XML
-
<repositories base-package="com.acme.repository" base-class="….MyRepositoryImpl" />