原标题:Spring认证中国教育管理中心-Spring Data Couchbase教程六(Spring中国教育管理中心)
4.6.Spring Data Repository 的自定义实现
Spring Data 提供了各种选项来创建几乎不需要编码的查询方法。但是当这些选项不能满足您的需求时,您还可以为存储库方法提供您自己的自定义实现。本节介绍如何执行此操作。
4.6.1.自定义单个存储库
要使用自定义功能丰富存储库,您必须首先为自定义功能定义片段接口和实现,如下所示:
示例 50. 自定义存储库功能的接口
interface CustomizedUserRepository {
void someCustomMethod(User user);
}
示例 51. 自定义存储库功能的实现
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {
public void someCustomMethod(User user) {
// Your custom implementation
}
}
与片段接口对应的类名中最重要的部分是Impl后缀。
实现本身不依赖于 Spring Data,可以是常规的 Spring bean。因此,您可以使用标准的依赖注入行为来注入对其他 bean 的引用(例如 a JdbcTemplate),参与切面等等。
然后可以让你的repository接口扩展fragment接口,如下:
示例 52. 对存储库界面的更改
interface UserRepository extends CrudRepository<User, Long>, CustomizedUserRepository {
// Declare query methods here
}
使用存储库接口扩展片段接口结合了 CRUD 和自定义功能,并使其可供客户端使用。
Spring Data 存储库是通过使用构成存储库组合的片段来实现的。片段是基础存储库、功能方面(例如QueryDsl)和自定义接口及其实现。每次将接口添加到存储库接口时,都会通过添加片段来增强组合。基本存储库和存储库方面的实现由每个 Spring Data 模块提供。
以下示例显示了自定义接口及其实现:
示例 53. 片段及其实现
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:
示例 54. 对存储库界面的更改
interface UserRepository extends CrudRepository<User, Long>, HumanRepository, ContactRepository {
// Declare query methods here
}
存储库可能由多个按其声明顺序导入的自定义实现组成。自定义实现具有比基本实现和存储库方面更高的优先级。此排序允许您覆盖基本存储库和方面方法,并在两个片段贡献相同的方法签名时解决歧义。存储库片段不限于在单个存储库界面中使用。多个存储库可以使用一个片段接口,让您可以在不同的存储库中重用自定义。
以下示例显示了存储库片段及其实现:
示例 55. 片段覆盖 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
}
}
以下示例显示了使用上述存储库片段的存储库:
示例 56. 自定义存储库接口
interface UserRepository extends CrudRepository<User, Long>, CustomizedSave<User> {
}
interface PersonRepository extends CrudRepository<Person, Long>, CustomizedSave<Person> {
}
配置
如果您使用命名空间配置,存储库基础结构会尝试通过扫描找到存储库的包下的类来自动检测自定义实现片段。这些类需要遵循将命名空间元素的repository-impl-postfix属性附加到片段接口名称的命名约定。此后缀默认为Impl. 以下示例显示了使用默认后缀的存储库和为后缀设置自定义值的存储库:
示例 57. 配置示例
<repositories base-package="com.acme.repository" />
<repositories base-package="com.acme.repository" repository-impl-postfix="MyPostfix" />
前面示例中的第一个配置尝试查找一个称为
com.acme.repository.CustomizedUserRepositoryImpl作为自定义存储库实现的类。第二个示例尝试查找com.acme.repository.CustomizedUserRepositoryMyPostfix。
歧义的解决
如果在不同的包中找到多个具有匹配类名的实现,Spring Data 会使用 bean 名称来识别要使用哪一个。
鉴于CustomizedUserRepository前面所示的以下两个自定义实现,使用第一个实现。它的 bean 名称
customizedUserRepositoryImpl与片段 interface( CustomizedUserRepository) 加上后缀的名称相匹配Impl。
示例 58. 歧义实现的解决
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 名称 plusImpl与 中为存储库实现定义的名称相匹配com.acme.impl.two,并且使用它而不是第一个。
手动接线
如果您的自定义实现仅使用基于注释的配置和自动装配,那么前面显示的方法效果很好,因为它被视为任何其他 Spring bean。如果你的实现片段 bean 需要特殊的连接,你可以声明这个 bean 并根据上一节中描述的约定命名它。然后,基础设施按名称引用手动定义的 bean 定义,而不是自己创建一个。以下示例显示了如何手动连接自定义实现:
示例 59. 自定义实现的手动连接
<repositories base-package="com.acme.repository" />
<beans:bean id="userRepositoryImpl" class="…">
<!-- further configuration -->
</beans:bean>
4.6.2.自定义基础存储库
当您想要自定义基本存储库行为以便影响所有存储库时,上一节中描述的方法需要自定义每个存储库接口。要改为更改所有存储库的行为,您可以创建一个扩展特定于持久性技术的存储库基类的实现。然后,此类充当存储库代理的自定义基类,如以下示例所示:
示例 60. 自定义存储库基类
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或一个模板类)的构造函数。
最后一步是让 Spring Data 基础设施了解定制的存储库基类。在 Java 配置中,您可以使用注解的repositoryBaseClass属性来执行此操作@Enable${store}Repositories,如下例所示:
示例 61. 使用 JavaConfig 配置自定义存储库基类
@Configuration
@EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class)
class ApplicationConfiguration { … }
XML 命名空间中提供了相应的属性,如以下示例所示:
示例 62. 使用 XML 配置自定义存储库基类
<repositories base-package="com.acme.repository"
base-class="….MyRepositoryImpl" />
4.7.从聚合根发布事件
存储库管理的实体是聚合根。在领域驱动设计应用程序中,这些聚合根通常发布领域事件。Spring Data 提供了一个注解@DomainEvents,您可以在聚合根的方法上使用该注解,以使发布尽可能简单,如以下示例所示:
示例 63. 从聚合根公开域事件
class AnAggregateRoot {
@DomainEvents
Collection<Object> domainEvents() {
// … return events you want to get published here
}
@AfterDomainEventPublication
void callbackMethod() {
// … potentially clean up domain events list
}
}
使用的方法@DomainEvents可以返回单个事件实例或事件集合。它不能有任何论据。
在所有事件都发布后,我们有一个用 注释的方法@
AfterDomainEventPublication。您可以使用它来潜在地清理要发布的事件列表(以及其他用途)。
该方法被称为一个Spring数据存储库的每一次一个save(…),saveAll(…),delete(…)或deleteAll(…)方法被调用。
4.8.Spring 数据扩展
本节记录了一组 Spring Data 扩展,这些扩展支持 Spring Data 在各种上下文中的使用。目前,大部分集成都是针对 Spring MVC 的。
4.8.1.Querydsl 扩展
Querydsl是一个框架,可以通过其流畅的 API 构建静态类型的类似 SQL 的查询。
几个 Spring Data 模块通过 Querydsl 提供集成QuerydslPredicateExecutor,如以下示例所示:
例
64.QuerydslPredicateExecutor 接口
public interface QuerydslPredicateExecutor<T> {
Optional<T> findById(Predicate predicate);
Iterable<T> findAll(Predicate predicate);
long count(Predicate predicate);
boolean exists(Predicate predicate);
// … more functionality omitted.
}
查找并返回与 匹配的单个实体Predicate。
查找并返回与 匹配的所有实体Predicate。
返回与 匹配的实体数Predicate。
返回匹配的实体是否Predicate存在。
要使用 Querydsl 支持,请QuerydslPredicateExecutor在存储库接口上进行扩展,如以下示例所示:
示例 65. 存储库上的 Querydsl 集成
interface UserRepository extends CrudRepository<User, Long>, QuerydslPredicateExecutor<User> {
}
前面的示例允许您使用 QuerydslPredicate实例编写类型安全的查询,如以下示例所示:
Predicate predicate = user.firstname.equalsIgnoreCase("dave")
.and(user.lastname.startsWithIgnoreCase("mathews"));
userRepository.findAll(predicate);