原标题:Spring认证中国教育管理中心-Spring Data Elasticsearch教程五(Spring中国教育管理中心)
10. 实体回调Spring Data 基础设施提供了在调用某些方法之前和之后修改实体的钩子。那些所谓EntityCallback的实例提供了一种方便的方式来检查和潜在地修改回调风格的实体。
AnEntityCallback看起来很像专门的ApplicationListener. BeforeSaveEvent一些 Spring Data 模块发布允许修改给定实体的存储特定事件(例如)。在某些情况下,例如在使用不可变类型时,这些事件可能会导致麻烦。此外,事件发布依赖于
ApplicationEventMulticaster. 如果使用异步配置TaskExecutor它可能会导致不可预测的结果,因为事件处理可以分叉到线程上。
实体回调提供具有同步和反应式 API 的集成点,以保证在处理链中定义明确的检查点按顺序执行,返回可能修改的实体或反应式包装器类型。
实体回调通常按 API 类型分隔。这种分离意味着同步 API 只考虑同步实体回调,而反应式实现只考虑反应式实体回调。
Spring Data Commons 2.2 引入了实体回调 API。这是应用实体修改的推荐方式。在调用可能注册的实例之前ApplicationEvents,仍会发布特定于现有商店的信息。EntityCallback
10.1实现实体回调AnEntityCallback通过其泛型类型参数直接与其域类型相关联。每个 Spring Data 模块通常附带一组EntityCallback涵盖实体生命周期的预定义接口。
示例 89. 解剖学 EntityCallback
@FunctionalInterface public interface BeforeSaveCallbackextends EntityCallback { T onBeforeSave(T entity <2>, String collection <3>); }
BeforeSaveCallback在保存实体之前调用的特定方法。返回一个可能修改过的实例。
在持久化之前的实体。
许多存储特定参数,例如实体持久保存到的集合。
示例 90. 反应式剖析 EntityCallback
@FunctionalInterface public interface ReactiveBeforeSaveCallbackextends EntityCallback { Publisher onBeforeSave(T entity <2>, String collection <3>); }
BeforeSaveCallback在保存实体之前,在订阅时调用的特定方法。发出一个可能修改过的实例。
在持久化之前的实体。
许多存储特定参数,例如实体持久保存到的集合。
可选的实体回调参数由实现的 Spring Data 模块定义,并从EntityCallback.callback().
实现适合您的应用程序需求的接口,如下例所示:
示例 91. 示例 BeforeSaveCallback
class DefaultingEntityCallback implements BeforeSaveCallback, Ordered { @Override public Object onBeforeSave(Person entity, String collection) { if(collection == "user") { return // ... } return // ... } @Override public int getOrder() { return 100; } }
根据您的要求实现回调。
如果存在多个相同域类型的实体回调,则可能会订购实体回调。排序遵循最低优先级。
10.2.注册实体回调EntityCallbackbean 被商店特定的实现拾取,以防它们在ApplicationContext. 大多数模板 API 已经实现ApplicationContextAware,因此可以访问ApplicationContext
以下示例说明了一组有效的实体回调注册:
示例 92. 示例EntityCallbackBean 注册
@Order(1) @Component class First implements BeforeSaveCallback{ @Override public Person onBeforeSave(Person person) { return // ... } } @Component class DefaultingEntityCallback implements BeforeSaveCallback , Ordered { @Override public Object onBeforeSave(Person entity, String collection) { // ... } @Override public int getOrder() { return 100; } } @Configuration public class EntityCallbackConfiguration { @Bean BeforeSaveCallback unorderedLambdaReceiverCallback() { return (BeforeSaveCallback ) it -> // ... } } @Component class UserCallbacks implements BeforeConvertCallback , BeforeSaveCallback { @Override public Person onBeforeConvert(User user) { return // ... } @Override public Person onBeforeSave(User user) { return // ... } }
BeforeSaveCallback从@Order注释中接收它的命令。
BeforeSaveCallbackOrdered通过接口实现接收其订单。
BeforeSaveCallback使用 lambda 表达式。默认无序,最后调用。请注意,由 lambda 表达式实现的回调不会公开类型信息,因此使用不可分配的实体调用这些会影响回调吞吐量。使用classorenum为回调 bean 启用类型过滤。
在单个实现类中组合多个实体回调接口。
10.3.Elasticsearch EntityCallbacksSpring Data Elasticsearch 在EntityCallback内部使用 API 来提供审计支持,并对以下回调做出反应:
11. Join-Type 实现Spring Data Elasticsearch 支持Join 数据类型,用于创建相应的索引映射并存储相关信息。
11.1。设置数据对于要在父子连接关系中使用的实体,它必须具有JoinField必须注释的类型属性。让我们假设一个Statement实体,其中的语句可能是问题、答案、评论或投票(此示例中还显示了Builder,它不是必需的,但稍后在示例代码中使用):
@document(indexName = "statements")
@Routing("routing")
public class Statement {
@Id
private String id;
@Field(type = FieldType.Text)
private String text;
@Field(type = FieldType.Keyword)
private String routing;
@JoinTypeRelations(
relations =
{
@JoinTypeRelation(parent = "question", children = {"answer", "comment"}),
@JoinTypeRelation(parent = "answer", children = "vote")
}
)
private JoinField relation;
private Statement() {
}
public static StatementBuilder builder() {
return new StatementBuilder();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getRouting() {
return routing;
}
public void setRouting(Routing routing) {
this.routing = routing;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public JoinField getRelation() {
return relation;
}
public void setRelation(JoinField relation) {
this.relation = relation;
}
public static final class StatementBuilder {
private String id;
private String text;
private String routing;
private JoinField relation;
private StatementBuilder() {
}
public StatementBuilder withId(String id) {
this.id = id;
return this;
}
public StatementBuilder withRouting(String routing) {
this.routing = routing;
return this;
}
public StatementBuilder withText(String text) {
this.text = text;
return this;
}
public StatementBuilder withRelation(JoinField relation) {
this.relation = relation;
return this;
}
public Statement build() {
Statement statement = new Statement();
statement.setId(id);
statement.setRouting(routing);
statement.setText(text);
statement.setRelation(relation);
return statement;
}
}
}
有关路由相关信息,请参阅路由值
一个问题可以有答案和评论
一个答案可以有投票权
该JoinField属性用于将关系的名称(问题、答案、评论或投票)与父 ID 结合起来。泛型类型必须与带@Id注释的属性相同。
Spring Data Elasticsearch 将为此类构建以下映射:
{
"statements": {
"mappings": {
"properties": {
"_class": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"routing": {
"type": "keyword"
},
"relation": {
"type": "join",
"eager_global_ordinals": true,
"relations": {
"question": [
"answer",
"comment"
],
"answer": "vote"
}
},
"text": {
"type": "text"
}
}
}
}
}
11.2.存储数据
给定这个类的存储库,以下代码插入一个问题、两个答案、一个评论和一个投票:
void init() {
repository.deleteAll();
Statement savedWeather = repository.save(
Statement.builder()
.withText("How is the weather?")
.withRelation(new JoinField<>("question"))
.build());
Statement sunnyAnswer = repository.save(
Statement.builder()
.withText("sunny")
.withRelation(new JoinField<>("answer", savedWeather.getId()))
.build());
repository.save(
Statement.builder()
.withText("rainy")
.withRelation(new JoinField<>("answer", savedWeather.getId()))
.build());
repository.save(
Statement.builder()
.withText("I don't like the rain")
.withRelation(new JoinField<>("comment", savedWeather.getId()))
.build());
repository.save(
Statement.builder()
.withText("+1 for the sun")
,withRouting(savedWeather.getId())
.withRelation(new JoinField<>("vote", sunnyAnswer.getId()))
.build());
}
创建问题陈述
问题的第一个答案
第二个答案
对问题的评论
投票给第一个答案,这需要将路由设置为天气文档,请参阅路由值。
11.3.检索数据目前,必须使用本机搜索查询来查询数据,因此标准存储库方法不支持。可以改为使用Spring Data Repositories 的自定义实现。
以下代码作为示例显示了如何使用实例检索所有有投票的条目(必须是answers,因为只有答案才能有投票)ElasticsearchOperations:
SearchHitshasVotes() { NativeSearchQuery query = new NativeSearchQueryBuilder() .withQuery(hasChildQuery("vote", matchAllQuery(), ScoreMode.None)) .build(); return operations.search(query, Statement.class); }



