原标题:Spring认证中国教育管理中心-Spring Data R2DBC框架教程四(Spring中国教育管理中心)
Spring Data 基础设施提供了在调用某些方法之前和之后修改实体的钩子。那些所谓的EntityCallback实例提供了一种方便的方法来检查和潜在地以回调风格修改实体。
AnEntityCallback看起来很像一个专门的ApplicationListener. 一些 Spring Data 模块发布BeforeSaveEvent允许修改给定实体的存储特定事件(例如)。在某些情况下,例如使用不可变类型时,这些事件可能会导致麻烦。此外,事件发布依赖于
ApplicationEventMulticaster. 如果使用异步配置TaskExecutor它可能会导致不可预测的结果,因为事件处理可以分叉到线程上。
实体回调为同步 API 和反应式 API 提供集成点,以保证在处理链中定义明确的检查点按顺序执行,返回潜在修改的实体或反应式包装器类型。
实体回调通常按 API 类型分隔。这种分离意味着同步 API 仅考虑同步实体回调,而反应式实现仅考虑反应式实体回调。
Spring Data Commons 2.2 引入了实体回调 API。这是应用实体修改的推荐方式。在调用可能已注册的实例之前,ApplicationEvents仍会发布特定于现有商店的信息。EntityCallback
AnEntityCallback通过其泛型类型参数直接与其域类型相关联。每个 Spring Data 模块通常带有一组EntityCallback涵盖实体生命周期的预定义接口。
例 76. 解剖 EntityCallback
@FunctionalInterfacepublic interface BeforeSaveCallback<T> extends EntityCallback<T> { /** * Entity callback method invoked before a domain object is saved. * Can return either the same or a modified instance. * * @return the domain object to be persisted. */ T onBeforeSave(T entity <2>, String collection <3>); }
BeforeSaveCallback在保存实体之前要调用的特定方法。返回一个可能被修改的实例。
在持久化之前的实体。
许多存储特定参数,例如实体持久化到的集合。
例 77. 反应式的剖析 EntityCallback
@FunctionalInterfacepublic interface ReactiveBeforeSaveCallback<T> extends EntityCallback<T> { /** * Entity callback method invoked on subscription, before a domain object is saved. * The returned Publisher can emit either the same or a modified instance. * * @return Publisher emitting the domain object to be persisted. */ Publisher<T> onBeforeSave(T entity <2>, String collection <3>); }
BeforeSaveCallback在保存实体之前,在订阅时调用的特定方法。发出一个可能被修改的实例。
在持久化之前的实体。
许多存储特定参数,例如实体持久化到的集合。
可选的实体回调参数由实现 Spring Data 模块定义并从EntityCallback.callback().
实现适合您的应用程序需求的接口,如下例所示:
示例 78. 示例 BeforeSaveCallback
class DefaultingEntityCallback implements BeforeSaveCallback<Person>, Ordered { @Override public Object onBeforeSave(Person entity, String collection) { if(collection == "user") { return // ... } return // ... } @Override public int getOrder() { return 100; } }
根据您的要求实现回调。
如果存在多个相同域类型的实体回调,则可能对实体回调进行排序。排序遵循最低优先级。
EntityCallback如果 bean 在ApplicationContext. 大多数模板 API 已经实现ApplicationContextAware,因此可以访问ApplicationContext
以下示例解释了一组有效的实体回调注册:
示例 79. EntityCallbackBean 注册示例
@Order(1) @Componentclass First implements BeforeSaveCallback<Person> { @Override public Person onBeforeSave(Person person) { return // ... } }@Componentclass DefaultingEntityCallback implements BeforeSaveCallback<Person>, Ordered { @Override public Object onBeforeSave(Person entity, String collection) { // ... } @Override public int getOrder() { return 100; } }@Configurationpublic class EntityCallbackConfiguration { @Bean BeforeSaveCallback<Person> unorderedLambdaReceiverCallback() { return (BeforeSaveCallback<Person>) it -> // ... } }@Componentclass UserCallbacks implements BeforeConvertCallback<User>, BeforeSaveCallback<User> { @Override public Person onBeforeConvert(User user) { return // ... } @Override public Person onBeforeSave(User user) { return // ... } }
BeforeSaveCallback从@Order注释中接收其命令。
BeforeSaveCallback通过Ordered接口实现接收其订单。
BeforeSaveCallback使用 lambda 表达式。默认情况下无序并最后调用。请注意,由 lambda 表达式实现的回调不会公开类型信息,因此使用不可分配的实体调用这些会影响回调吞吐量。使用classorenum为回调 bean 启用类型过滤。
在单个实现类中组合多个实体回调接口。
Spring Data R2DBC 使用EntityCallbackAPI 作为其审计支持并对以下回调做出反应。
当使用多个可能不同的数据库时,您的应用程序将需要不同的配置方法。所提供的
AbstractR2dbcConfiguration支持类假定单个ConnectionFactory从中Dialect得到的。话虽如此,您需要自己定义一些 bean 来配置 Spring Data R2DBC 以处理多个数据库。
R2DBC 存储库需要R2dbcEntityOperations实现存储库。无需使用即可扫描存储库的简单配置
AbstractR2dbcConfiguration如下所示:
@Configuration@EnableR2dbcRepositories(basePackages = "com.acme.mysql", entityOperationsRef = "mysqlR2dbcEntityOperations")static class MySQLConfiguration { @Bean @Qualifier("mysql") public ConnectionFactory mysqlConnectionFactory() { return … } @Bean public R2dbcEntityOperations mysqlR2dbcEntityOperations(@Qualifier("mysql") ConnectionFactory connectionFactory) { DatabaseClient databaseClient = DatabaseClient.create(connectionFactory); return new R2dbcEntityTemplate(databaseClient, MySqlDialect.INSTANCE); } }
请注意,@EnableR2dbcRepositories允许通过databaseClientRef或进行配置entityOperationsRef。DatabaseClient连接到多个相同类型的数据库时,使用各种bean 很有用。当使用方言不同的不同数据库系统时,请改用@EnableR2dbcRepositories(entityOperationsRef = ...)`。
Spring Data 提供了复杂的支持,以透明地跟踪谁创建或更改了实体以及更改发生的时间。要从该功能中受益,您必须为实体类配备审计元数据,这些元数据可以使用注释或通过实现接口来定义。此外,必须通过 Annotation 配置或 XML 配置启用审计以注册所需的基础架构组件。有关配置示例,请参阅特定于商店的部分。
仅跟踪创建和修改日期的应用程序不需要指定AuditorAware.
15.1.1.基于注解的审计元数据
我们提供@CreatedBy并@LastModifiedBy捕获创建或修改实体的用户,@CreatedDate并@LastModifiedDate捕获更改发生的时间。
示例 80. 一个被审计的实体
class Customer { @CreatedBy private User user; @CreatedDate private Instant createdDate; // … further properties omitted}
如您所见,可以有选择地应用注释,具体取决于您要捕获的信息。进行更改时捕获的注释可用于 Joda-Time DateTime、旧版 JavaDate和Calendar、JDK8 日期和时间类型以及long或类型的属性Long。
审计元数据不一定需要存在于根级实体中,但可以添加到嵌入式实体中(取决于实际使用的存储),如下面的截图所示。
示例 81. 审计嵌入实体中的元数据
class Customer { private AuditMetadata auditingMetadata; // … further properties omitted}class AuditMetadata { @CreatedBy private User user; @CreatedDate private Instant createdDate; }
如果您不想使用注释来定义审计元数据,您可以让您的域类实现该Auditable接口。它公开了所有审计属性的 setter 方法。
如果您使用@CreatedBy或@LastModifiedBy,审计基础结构需要以某种方式了解当前主体。为此,我们提供了一个AuditorAware<T>SPI 接口,您必须实现该接口以告知基础设施当前与应用程序交互的用户或系统是谁。泛型类型T定义了用什么类型注释的属性@CreatedBy或@LastModifiedBy必须是什么类型。
以下示例显示了使用 Spring SecurityAuthentication对象的接口的实现:
Example 82.AuditorAware基于 Spring Security 的实现
class SpringSecurityAuditorAware implements AuditorAware<User> { @Override public Optional<User> getCurrentAuditor() { return Optional.ofNullable(SecurityContextHolder.getContext()) .map(SecurityContext::getAuthentication) .filter(Authentication::isAuthenticated) .map(Authentication::getPrincipal) .map(User.class::cast); } }
该实现访问AuthenticationSpring Security 提供的对象并查找UserDetails您在UserDetailsService实现中创建的自定义实例。我们在这里假设您通过UserDetails实现公开域用户,但根据Authentication发现,您也可以从任何地方查找它。
使用反应式基础架构时,您可能希望使用上下文信息来提供@CreatedBy或提供@LastModifiedBy信息。我们提供了一个ReactiveAuditorAware<T>SPI 接口,您必须实现该接口才能告诉基础设施当前与应用程序交互的用户或系统是谁。泛型类型T定义了用什么类型注释的属性@CreatedBy或@LastModifiedBy必须是什么类型。
以下示例显示了使用响应式 Spring SecurityAuthentication对象的接口的实现:
例 83.ReactiveAuditorAware基于 Spring Security 的实现
class SpringSecurityAuditorAware implements ReactiveAuditorAware<User> { @Override public Mono<User> getCurrentAuditor() { return ReactiveSecurityContextHolder.getContext() .map(SecurityContext::getAuthentication) .filter(Authentication::isAuthenticated) .map(Authentication::getPrincipal) .map(User.class::cast); } }
该实现访问AuthenticationSpring Security 提供的对象并查找UserDetails您在UserDetailsService实现中创建的自定义实例。我们在这里假设您通过UserDetails实现公开域用户,但根据Authentication发现,您也可以从任何地方查找它。