生命周期事件
Cassandra 映射框架内置了若干 org.springframework.context.ApplicationEvent 事件,您的应用可以通过在 ApplicationContext 中注册特殊 bean 来响应它们。由于基于 Spring 的应用上下文事件基础设施构建,其他产品(如 Spring Integration)可以轻松接收这些事件,因为它们是基于 Spring 的应用中广为人知的事件机制。
要在对象存入数据库之前拦截它,您可以注册 AbstractCassandraEventListener 的子类,并重写 onBeforeSave(…) 方法。事件派发时,您的监听器会被调用,并传入领域对象(即一个 Java 实体)。实体生命周期事件可能开销较大,加载大型结果集时您可能会注意到性能特征的变化。您可以在 Template API 上禁用生命周期事件。以下示例使用了 onBeforeSave 方法:
class BeforeSaveListener extends AbstractCassandraEventListener<Person> {
@Override
public void onBeforeSave(BeforeSaveEvent<Person> event) {
// … change values, delete them, whatever …
}
}
在 Spring ApplicationContext 中声明这些 bean,即可在事件派发时调用它们。
AbstractCassandraEventListener 具有以下回调方法:
-
onBeforeSave:在CassandraTemplate.insert(…)和.update(…)操作中调用,时机为在数据库中插入或更新一行之前、创建Statement之后。 -
onAfterSave:在CassandraTemplate…insert(…)和.update(…)操作中调用,时机为在数据库中插入或更新一行之后。 -
onBeforeDelete:在CassandraTemplate.delete(…)操作中调用,时机为从数据库删除行之前。 -
onAfterDelete:在CassandraTemplate.delete(…)操作中调用,时机为从数据库删除行之后。 -
onAfterLoad:在CassandraTemplate.select(…)、.slice(…)和.stream(…)方法中调用,时机为从数据库检索到每一行之后。 -
onAfterConvert:在CassandraTemplate.select(…)、.slice(…)和.stream(…)方法中调用,时机为将从数据库检索到的行转换为 POJO 之后。
生命周期事件仅针对根级类型发布。用作聚合根内属性的复杂类型不会触发事件发布。
实体回调
Spring Data 基础设施提供了在某些方法调用前后修改实体的钩子。这些所谓的 EntityCallback 实例提供了一种便捷方式,以回调风格检查并可能修改实体。
EntityCallback 看起来很像一个专用的 ApplicationListener。一些 Spring Data 模块会发布特定存储的事件(如 BeforeSaveEvent),允许修改给定实体。在某些情况下(例如处理不可变类型时),这些事件可能引发问题。此外,事件发布依赖于 ApplicationEventMulticaster。如果为其配置了异步 TaskExecutor,事件处理可能被分派到某个 Thread 上执行,从而导致不可预测的结果。
实体回调提供了与同步和响应式 API 的集成点,以保证在处理链中明确定义的检查点处按顺序执行,并返回可能被修改的实体或响应式包装类型。
实体回调通常按 API 类型区分。这种区分意味着同步 API 只考虑同步实体回调,而响应式实现只考虑响应式实体回调。
Entity Callback API 随 Spring Data Commons 2.2 引入。它是应用实体修改的推荐方式。现有的特定存储 ApplicationEvents 仍会在调用可能已注册的 EntityCallback 实例之前发布。
实现实体回调
EntityCallback 通过其泛型类型参数直接关联到对应的领域类型。每个 Spring Data 模块通常都会附带一组预定义的 EntityCallback 接口,覆盖实体生命周期。
@FunctionalInterface
public 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.
*/
1
T onBeforeSave(T entity, 2
String collection); 3
}
BeforeSaveCallback的专用方法,在实体保存前调用。返回一个可能被修改的实例。即将持久化的实体。
若干特定存储的参数,例如实体要持久化到的 collection。
@FunctionalInterface
public 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.
*/
1
Publisher<T> onBeforeSave(T entity, 2
String collection); 3
}
BeforeSaveCallback的专用方法,在订阅时、实体保存前调用。发射一个可能被修改的实例。即将持久化的实体。
若干特定存储的参数,例如实体要持久化到的 collection。
可选的实体回调参数由实现它的 Spring Data 模块定义,并从 EntityCallback.callback() 的调用点推断得出。
请根据您的应用需要实现合适的接口,如下例所示:
class DefaultingEntityCallback implements BeforeSaveCallback<Person>, Ordered { 2
@Override
public Object onBeforeSave(Person entity, String collection) { 1
if("user".equals(collection)) {
return …;
}
return …;
}
@Override
public int getOrder() {
return 100; 2
}
}
按您的需求实现的回调。
如果同一领域类型存在多个实体回调,可以对其排序。排序遵循最低优先级原则。
注册实体回调
如果 EntityCallback bean 注册在 ApplicationContext 中,特定存储的实现会自动获取它们。大多数模板 API 已经实现了 ApplicationContextAware,因此可以访问 ApplicationContext
以下示例说明了一组有效的实体回调注册方式:
@Order(1) 1
@Component
class First implements BeforeSaveCallback<Person> {
@Override
public Person onBeforeSave(Person person) {
return // ...
}
}
@Component
class DefaultingEntityCallback implements BeforeSaveCallback<Person>,
Ordered { 2
@Override
public Object onBeforeSave(Person entity, String collection) {
// ...
}
@Override
public int getOrder() {
return 100; 2
}
}
@Configuration
public class EntityCallbackConfiguration {
@Bean
BeforeSaveCallback<Person> unorderedLambdaReceiverCallback() { 3
return (BeforeSaveCallback<Person>) it -> // ...
}
}
@Component
class UserCallbacks implements BeforeConvertCallback<User>,
BeforeSaveCallback<User> { 4
@Override
public Person onBeforeConvert(User user) {
return // ...
}
@Override
public Person onBeforeSave(User user) {
return // ...
}
}
通过
@Order注解指定顺序的BeforeSaveCallback。通过实现
Ordered接口指定顺序的BeforeSaveCallback。使用 lambda 表达式的
BeforeSaveCallback。默认无序,最后调用。请注意,由 lambda 表达式实现的回调不暴露类型信息,因此用不可赋值的实体调用这些回调会影响回调吞吐量。请使用class或enum以启用回调 bean 的类型过滤。在单个实现类中组合多个实体回调接口。
特定存储的 EntityCallbacks
Spring Data for Apache Cassandra 使用 EntityCallback API 来提供审计支持,并响应以下回调。
表 1. 支持的实体回调
| 回调 | 方法 | 描述 | 顺序 |
|---|---|---|---|
ReactiveBeforeConvertCallback BeforeConvertCallback | onBeforeConvert(T entity, CqlIdentifier tableName) | 在领域对象转换为 Statement 之前调用。可更新领域对象,使更改反映在 Statement 中。 | Ordered.LOWEST_PRECEDENCE |
ReactiveAuditingEntityCallback AuditingEntityCallback | onBeforeConvert(Object entity, CqlIdentifier tableName) | 将可审计实体标记为 已创建 或 已修改 | 100 |
ReactiveBeforeSaveCallback BeforeSaveCallback | onBeforeSave(T entity, CqlIdentifier tableName, Statement statement) | 在领域对象保存之前调用。 可在 Statement 创建之后修改目标对象。所提供的语句包含所有已映射的实体信息,但对领域对象的更改不包含在 Statement 中。 | Ordered.LOWEST_PRECEDENCE |