原题目:Spring 认证 | 应用 Spring Data Repositories(下)起源:(Spring 中国教育管理中心)
4.5.2. Java 配置
您还能够通过 @Enable$Repositories 在 Java 配置类上应用特定于商店的正文来触发存储库根底构造。无关 Spring 容器的基于 Java 的配置的介绍,请参阅 Spring 参考文档中的 JavaConfig。
启用 Spring Data 存储库的示例配置相似于以下内容:
示例 29. 基于注解的存储库配置示例
@Configuration
@EnableJpaRepositories(“com.acme.repositories”)
class ApplicationConfiguration {
@Bean
EntityManagerFactory entityManagerFactory() {
// …
}
}
后面的示例应用特定于 JPA 的正文,您能够依据理论应用的商店模块更改该正文。这同样实用于 EntityManagerFactorybean 的定义。请参阅涵盖商店特定配置的局部。
4.5.3. 独立应用
您还能够在 Spring 容器之外应用存储库基础设施——例如,在 CDI 环境中。您的类门路中依然须要一些 Spring 库,但通常,您也能够通过编程形式设置存储库。提供存储库反对的 Spring Data 模块附带了 RepositoryFactory 您能够应用的特定于持久性技术的技术,如下所示:
示例 30. 存储库工厂的独立应用
RepositoryFactorySupport factory = … // Instantiate factory here
UserRepository repository = factory.getRepository(UserRepository.class);
4.6. Spring Data Repositories 的自定义实现
Spring Data 提供了各种选项来创立只需很少编码的查询方法。然而当这些选项不合乎您的需要时,您还能够为存储库办法提供您本人的自定义实现。本节介绍如何做到这一点。
4.6.1. 自定义单个存储库
要应用自定义功能丰富存储库,您必须首先定义片段接口和自定义性能的实现,如下所示:
示例 31. 自定义存储库性能的接口
interface CustomizedUserRepository {
void someCustomMethod(User user);
}
示例 32. 自定义存储库性能的实现
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {
public void someCustomMethod(User user) {
// Your custom implementation
}
}
与片段接口对应的类名中最重要的局部是 Impl 后缀。
实现自身不依赖于 Spring Data,能够是一个一般的 Spring bean。因而,你能够应用规范的依赖注入行为来注入对其余 bean(例如 a JdbcTemplate)的援用,参加方面等等。
而后你能够让你的 repository 接口扩大 fragment 接口,如下:
示例 33. 对存储库界面的更改
interface UserRepository extends CrudRepository, CustomizedUserRepository {
// Declare query methods here
}
应用您的存储库接口扩大片段接口联合了 CRUD 和自定义性能,并使其可供客户端应用。
Spring Data 存储库是通过应用造成存储库组合的片段来实现的。片段是根底存储库、性能方面(例如 QueryDsl)和自定义接口及其实现。每次向存储库界面增加界面时,您都能够通过增加片段来加强组合。每个 Spring Data 模块都提供根本存储库和存储库方面的实现。
以下示例显示了自定义接口及其实现:
示例 34. 片段及其实现
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:
示例 35. 对存储库界面的更改
interface UserRepository extends CrudRepository, HumanRepository, ContactRepository {
// Declare query methods here
}
存储库可能由按申明程序导入的多个自定义实现组成。自定义实现比根本实现和存储库方面具备更高的优先级。如果两个片段奉献雷同的办法签名,则此排序容许您笼罩根本存储库和方面办法并解决歧义。存储库片段不限于在单个存储库界面中应用。多个存储库能够应用片段接口,让您能够在不同的存储库中重用自定义。
以下示例显示了存储库片段及其实现:
示例 36. 片段笼罩 save(…)
interface CustomizedSave {
S save(S entity);
}
class CustomizedSaveImpl implements CustomizedSave {
public S save(S entity) {
// Your custom implementation
}
}
以下示例显示了应用上述存储库片段的存储库:
示例 37. 自定义存储库接口
interface UserRepository extends CrudRepository, CustomizedSave {
}
interface PersonRepository extends CrudRepository, CustomizedSave {
}
配置
如果您应用命名空间配置,存储库根底构造会尝试通过扫描它在其中找到存储库的包下的类来自动检测自定义实现片段。这些类须要遵循将命名空间元素的 repository-impl-postfix 属性附加到片段接口名称的命名约定。此后缀默认为 Impl. 以下示例显示了一个应用默认后缀的存储库和一个为后缀设置自定义值的存储库:
示例 38. 配置示例
后面示例中的第一个配置尝试查找一个称为 com.acme.repository.CustomizedUserRepositoryImpl 作为自定义存储库实现的类。第二个示例尝试查找 com.acme.repository.CustomizedUserRepositoryMyPostfix.
解决歧义
如果在不同的包中找到多个具备匹配类名的实现,Spring Data 应用 bean 名称来标识应用哪个。
鉴于 CustomizedUserRepository 后面显示的以下两个自定义实现,应用第一个实现。它的 bean 名称是 customizedUserRepositoryImpl,它与片段 interface(CustomizedUserRepository) 加上后缀的名称相匹配 Impl。
示例 39. 解决不明确的实现
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,并应用它代替第一个名称。
手动接线
如果您的自定义实现仅应用基于注解的配置和主动拆卸,则后面显示的办法成果很好,因为它被视为任何其余 Spring bean。如果您的实现片段 bean 须要非凡连贯,您能够依据上一节中形容的约定申明 bean 并为其命名。而后,基础设施通过名称援用手动定义的 bean 定义,而不是本人创立一个。以下示例显示了如何手动连贯自定义实现:
示例 40. 自定义实现的手动接线
4.6.2. 自定义根底存储库
当您想要自定义根本存储库行为以便影响所有存储库时,上一节中形容的办法须要自定义每个存储库接口。要改为更改所有存储库的行为,您能够创立一个扩大持久性技术特定存储库基类的实现。而后,此类充当存储库代理的自定义基类,如以下示例所示:
示例 41. 自定义存储库基类
class MyRepositoryImpl
extends SimpleJpaRepository {
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 save(S entity) {
// implementation goes here
}
}
该类须要具备特定于商店的存储库工厂实现应用的超类的构造函数。如果存储库基类有多个构造函数,则笼罩一个 EntityInformation 加上存储特定根底构造对象(例如一个 EntityManager 或一个模板类)的构造函数。
最初一步是让 Spring Data 基础设施晓得定制的存储库基类。在 Java 配置中,您能够通过应用注解的 repositoryBaseClass 属性来实现 @Enable$Repositories,如下例所示:
示例 42. 应用 JavaConfig 配置自定义存储库基类
@Configuration
@EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class)
class ApplicationConfiguration {…}
XML 命名空间中提供了相应的属性,如以下示例所示:
示例 43. 应用 XML 配置自定义存储库基类
base-class=”….MyRepositoryImpl” />
4.7. 从聚合根公布事件
存储库治理的实体是聚合根。在域驱动设计应用程序中,这些聚合根通常公布域事件。Spring Data 提供了一个名为的正文 @DomainEvents,您能够在聚合根的办法上应用该正文,以使该公布尽可能简略,如以下示例所示:
示例 44. 从聚合根公开域事件
class AnAggregateRoot {
@DomainEvents
Collection 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,如以下示例所示:
例 45.QuerydslPredicateExecutor 接口
public interface QuerydslPredicateExecutor {
Optional findById(Predicate predicate);
Iterable findAll(Predicate predicate);
long count(Predicate predicate);
boolean exists(Predicate predicate);
// … more functionality omitted.
}
查找并返回与 匹配的单个实体 Predicate。
查找并返回与 匹配的所有实体 Predicate。
返回与 匹配的实体数 Predicate。
返回匹配的实体是否 Predicate 存在。
要应用 Querydsl 反对,请扩大 QuerydslPredicateExecutor 您的存储库界面,如以下示例所示:
示例 46. 存储库上的 Querydsl 集成
interface UserRepository extends CrudRepository, QuerydslPredicateExecutor {
}
后面的示例容许您应用 QuerydslPredicate 实例编写类型平安的查问,如以下示例所示:
Predicate predicate = user.firstname.equalsIgnoreCase(“dave”)
.and(user.lastname.startsWithIgnoreCase(“mathews”));
userRepository.findAll(predicate);
4.8.2. 网络反对
反对存储库编程模型的 Spring Data 模块附带了各种 Web 反对。Web 相干组件要求 Spring MVC JAR 位于类门路上。其中一些甚至提供与 Spring HATEOAS 的集成。通常,通过应用 @EnableSpringDataWebSupportJavaConfig 配置类中的正文来启用集成反对,如以下示例所示:
示例 47. 启用 Spring Data Web 反对
@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
class WebConfiguration {}
该 @EnableSpringDataWebSupport 批注注册几个组件。咱们将在本节前面探讨这些。它还检测类门路上的 Spring HATEOAS 并为其注册集成组件(如果存在)。
或者,如果您应用 XML 配置,请注册 SpringDataWebConfiguration 或 HateoasAwareSpringDataWebConfiguration 作为 Spring bean,如以下示例所示(对于 SpringDataWebConfiguration):
示例 48. 在 XML 中启用 Spring Data Web 反对
根本网络反对
上一节中显示的配置注册了一些根本组件:
A 应用 DomainClassConverter 类让 Spring MVC 从申请参数或门路变量解析存储库治理的域类的实例。
HandlerMethodArgumentResolver 让 Spring MVC 从申请参数解析 Pageable 和 Sort 实例的实现。
Jackson Modules 用于反 / 序列化 Point 和 等类型 Distance,或存储特定类型,具体取决于所应用的 Spring 数据模块。
应用 DomainClassConverter 类
本 DomainClassConverter 类让你在 Spring MVC 中的控制器办法签名应用域类型间接使您不用手动通过资源库查找的状况下,如下例所示:
示例 49. 在办法签名中应用域类型的 Spring MVC 控制器
@Controller
@RequestMapping(“/users”)
class UserController {
@RequestMapping(“/”)
String showUserForm(@PathVariable(“id”) User user, Model model) {
model.addAttribute(“user”, user);
return “userForm”;
}
}
该办法 User 间接接管实例,不须要进一步查找。能够通过让 Spring MVCid 先将门路变量转换为域类的类型,最终通过调用 findById(…) 为域类型注册的存储库实例来拜访实例来解析实例。
目前,存储库必须实现 CrudRepository 能力被发现进行转换。
用于可分页和排序的 HandlerMethodArgumentResolvers
的配置片段中,在示出前一节还注册一个 PageableHandlerMethodArgumentResolver,以及实例 SortHandlerMethodArgumentResolver。注册启用 Pageable 并 Sort 作为无效的控制器办法参数,如以下示例所示:
示例 50. 应用 Pageable 作为控制器办法参数
@Controller
@RequestMapping(“/users”)
class UserController {
private final UserRepository repository;
UserController(UserRepository repository) {
this.repository = repository;
}
@RequestMapping
String showUsers(Model model, Pageable pageable) {
model.addAttribute(“users”, repository.findAll(pageable));
return “users”;
}
}
后面的办法签名导致 Spring MVC 尝试 Pageable 应用以下默认配置从申请参数派生实例:
要自定义此行为,请别离注册一个实现 PageableHandlerMethodArgumentResolverCustomizer 接口或 SortHandlerMethodArgumentResolverCustomizer 接口的 bean。它的 customize() 办法被调用,让您更改设置,如以下示例所示:
@Bean SortHandlerMethodArgumentResolverCustomizer sortCustomizer() {
return s -> s.setPropertyDelimiter(“”);
}
如果设置现有的属性 MethodArgumentResolver 不足以满足您的目标,请扩大 SpringDataWebConfiguration 或启用 HATEOAS 的等效项,笼罩 pageableResolver() 或 sortResolver() 办法,并导入您的自定义配置文件而不是应用 @Enable 正文。
如果您须要从申请中解析多个 Pageable 或多个 Sort 实例(例如,对于多个表),您能够应用 Spring 的 @Qualifier 注解来辨别一个和另一个。申请参数必须以 为前缀 $_。以下示例显示了生成的办法签名:
String showUsers(Model model,
@Qualifier("thing1") Pageable first,
@Qualifier("thing2") Pageable second) {…}
您必须填充 thing1_page、thing2_page 等。
Pageable 传递给办法的默认值等效于 a PageRequest.of(0, 20),但您能够通过应用参数 @PageableDefault 上的正文来自定义它 Pageable。