使用 Spring Data Repositories(下)

_原题是: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);
【使用 Spring Data Repositories(下)】// 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。

    推荐阅读