4.6. Spring Data库的自定义实现

4.6. Spring Data库的自定义实现

4.6. Custom Implementations for Spring Data Repositories

此章节覆盖了库的自定义与如何组合一个复合库。

This section covers repository customization and how fragments form a composite repository.

当一个查询方法需要不同的行为,或者无法由查询导出实现,就有必要提供一个自定义的实现。Spring Data库允许你提供一个自定义库并于泛型CRUD抽象和查询方法功能集成。

When a query method requires a different behavior or cannot be implemented by query derivation, then it is necessary to provide a custom implementation. Spring Data repositories let you provide custom repository code and integrate it with generic CRUD abstraction and query method functionality.

4.6.1. 自定义个体库

4.6.1. 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 shown in the following example:

例25. 自定义库功能的接口

Example 25. Interface for custom repository functionality

interface CustomizedUserRepository {
  void someCustomMethod(User user);
}

然后你可以让你的库接口额外继承此片段接口,如下例所示:

Then you can let your repository interface additionally extend from the fragment interface, as shown in the following example:

例26. 自定义库功能的实现

Example 26. Implementation of custom repository functionality

class CustomizedUserRepositoryImpl implements CustomizedUserRepository {

  public void someCustomMethod(User user) {
    // Your custom implementation
  }
}

此实现本身并不依赖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.

你可以使库接口继承此片段接口,如下例所示:

You can let your repository interface extend the fragment interface, as shown in the following example:

例27. 库接口的变更

Example 27. Changes to your repository interface

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 implementation. 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:

例28. 片段与其实现

Example 28. Fragments with 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:

例29. 库接口变更

Example 29. Changes to your repository interface

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:

例30. 覆写save(…)的片段

Example 30. Fragments overriding 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:

例31. 自定义库接口

Example 31. Customized repository interfaces

interface UserRepository extends CrudRepository<User, Long>, CustomizedSave<User> {
}

interface PersonRepository extends CrudRepository<Person, Long>, CustomizedSave<Person> {
}

配置

Configuration

如果你使用命名空间配置,库基础设施会通过尝试扫描包下面的类,来自动检测自定义实现片段,以此来发现一个库。这些类需要遵循命名规范,在片段接口名称中添加命名元素的repository-impl-postfix属性。默认的后缀为Impl。下面的例子展示了一个使用默认后缀的库,和一个设置了自定义后缀的库:

If you use namespace configuration, 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 the namespace element’s repository-impl-postfix attribute to the fragment interface name. This postfix defaults to Impl. The following example shows a repository that uses the default postfix and a repository that sets a custom value for the postfix:

例32. 配置样例

Example 32. Configuration example

<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 lookup 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.

例33. 歧义实现的消除

Example 33. Resolution of amibiguous implementations

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
}

如果在UserRepository接口上注解@Component("specialCustom"),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并根据前面的章节中描述的转换进行命名。则基础设置将会通过名称指向手动定义的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:

例34. 手动关联自定义实现

Example 34. Manual wiring of custom implementations

<repositories base-package="com.acme.repository" />

<beans:bean id="userRepositoryImpl" class="…">
  <!-- further configuration -->
</beans:bean>

4.6.2. 自定义基础库

4.6.2. Customize the Base Repository

上面章节描述的方法需要自定义每一个库接口,当你需要自定义基础库行为时,则会影响所有的库。为了不影响所有库的行为,你可以创建一个继承指定技术的持久化库基础类的实现。此类将作为库代理的自定义基础类,如下例所示:

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:

例35. 自定义库基础类

Example 35. Custom repository base class

class MyRepositoryImpl<T, ID extends Serializable> 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
  }
}

最后一步是让Spring Data基础设施知道这个自定义库基础类。在Java配置中,你可以使用@Enable${store}Repositories注解的repositoryBaseClass属性,如下例所示:

The final step is to make the Spring Data infrastructure aware of the customized repository base class. In Java configuration, you can do so by using the repositoryBaseClass attribute of the @Enable${store}Repositories annotation, as shown in the following example:

例36. 使用JavaConfig配置一个自定义库基础类

Example 36. Configuring a custom repository base class using JavaConfig

@Configuration
@EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class)
class ApplicationConfiguration { … }

在XML命名空间中也有对应属性,如下例所示:

A corresponding attribute is available in the XML namespace, as shown in the following example:

例37. 使用XML配置一个自定义库基础类

Example 37. Configuring a custom repository base class using XML

<repositories base-package="com.acme.repository" base-class="….MyRepositoryImpl" />

翻译部分版权归YahaCode团队所有。仅供学习研究之用,任何组织或个人不得私自以此用于任何形式的商业目的

发表评论