使用 @IndexingDependency derivedFrom 和 属性 桥
Using @IndexingDependency derivedFrom and a property bridge
我想将休眠搜索的 @IndexingDependency
与 PropertyBridge
一起使用,但我似乎无法让它工作。
我收到这个错误:
Hibernate ORM mapping: type 'com.something.Person': path '.currentStatus':
failures:
- HSEARCH700020: Unable to find the inverse side of the association on type
'com.something.Person' at path '.currentStatus<no value extractors>'. Hibernate Search
needs this information in order to reindex 'com.something.Person' when
'com.something.Status' is modified. You can solve this error by defining the inverse
side of this association, either with annotations specific to your integration
(@OneToMany(mappedBy = ...) in Hibernate ORM) or with the Hibernate Search
@AssociationInverseSide annotation. Alternatively, if you do not need to reindex
'com.something.Person' when 'com.something.Status' is modified, you can disable
automatic reindexing with @IndexingDependency(reindexOnUpdate = ReindexOnUpdate.SHALLOW)
不确定我是否做错了什么,或者我尝试做的事情是不可能的。感谢您的帮助。
这里是涉及到的文件。
Person.class
@Entity
@Table
@Indexed
public class Person {
@OneToMany(mappedBy = "patient", cascade = CascadeType.ALL)
private Set<Status> status = new HashSet<>();
@Transient
@StatusBinding(fieldName = "currentStatus")
@IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "status")))
public Status getCurrentStatus() {
return this.status.stream()
.filter(it -> it.getDate().isAfter(LocalDate.now()))
.max(Comparator.comparing(Status::getDate))
.orElse(null);
}
}
StatusBinding.class
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
@PropertyMapping(processor = @PropertyMappingAnnotationProcessorRef(type = StatusBinding.Processor.class))
@Documented
public @interface StatusBinding {
String fieldName() default "";
class Processor implements PropertyMappingAnnotationProcessor<StatusBinding> {
@Override
public void process(PropertyMappingStep mapping, StatusBindingannotation, PropertyMappingAnnotationProcessorContext context) {
StatusBinderbinder = new StatusBinder();
if (!annotation.fieldName().isBlank()) binder.setFieldName(annotation.fieldName());
mapping.binder(binder);
}
}
}
StatusBinder.class
public class StatusBinder implements PropertyBinder {
@Setter private String fieldName = "mainStatus";
@Override
public void bind(PropertyBindingContext context) {
context.dependencies()
.use("status")
.use("date")
.use("note");
IndexSchemaObjectField mainStatusField = context.indexSchemaElement().objectField(this.fieldName);
context.bridge(Status.class, new StatusBridge(
mainStatusField.toReference(),
mainStatusField.field("status", context.typeFactory().asString()).toReference(),
mainStatusField.field("date", context.typeFactory().asLocalDate()).toReference(),
mainStatusField.field("note", context.typeFactory().asString()).toReference()
));
}
private static class StatusBrige implements PropertyBridge<Status> {
private final IndexObjectFieldReference mainStatusField;
private final IndexFieldReference<String> statusField;
private final IndexFieldReference<LocalDate> dateField;
private final IndexFieldReference<String> noteField;
public StatusBrige(
IndexObjectFieldReference mainStatusField,
IndexFieldReference<String> statusField,
IndexFieldReference<LocalDate> dateField,
IndexFieldReference<String> noteField
) {
this.mainStatusField = mainStatusField;
this.statusField = statusField;
this.dateField = dateField;
this.noteField = noteField;
}
@Override
public void write(DocumentElement target, Status mainStatus, PropertyBridgeWriteContext context) {
DocumentElement statutElement = target.addObject(this.mainStatusField);
statutElement.addValue(this.statusField, mainStatus.getStatus);
statutElement.addValue(this.dateField, mainStatus.getDate());
statutElement.addValue(this.noteField, mainStatus.getNote());
}
}
}
问题
当 Status
实体被修改时,Hibernate Search 不知道如何检索具有 Status
作为其 currentStatus
的相应 Person
。
解决方案
假设currentStatus
总是包含在status
中,并且由于Status.patient
是Person.status
关联的反面,你应该只需要添加:
@Transient
@StatusBinding(fieldName = "currentStatus")
@IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "status")))
// ADD THIS ANNOTATION
@AssociationInverseSide(
inversePath = @ObjectPath(@PropertyValue(propertyName = "patient"))
)
public Status getCurrentStatus() {
// ...
}
为什么?
我会试着解释一下,但有点复杂,请耐心等待。
派生属性和关联的反面是相关概念:它们具有允许 Hibernate Search 执行自动重新索引的共同目的。
但是,它们仍然是不同的概念,Hibernate Search 无法从另一个推断出一个。
使用 @IndexingDependency(derivedFrom)
,您定义了 currentStatus
的计算依赖于:
@IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "status")))
public Status getCurrentStatus() {
这告诉 Hibernate Search,只要 status
属性 发生变化,currentStatus
就会发生变化。有了这些信息,Hibernate Search 就能够确定无论何时调用 person.getStatus().remove(...)
或 person.getStatus().add(...)
(例如),您的 Person
实体都需要重新索引,因为 currentStatus
已被索引,并且它可能改变了。
在您的自定义活页夹中,您也是 defining dependencies:
context.dependencies()
.use("status")
.use("date")
.use("note");
这告诉 Hibernate Search,每当 Status
实体的 status
、date
和 note
属性发生变化时,Person
具有 Status
因为它的 currentStatus
需要重建索引。
但是...Hibernate Search 不知道的是如何检索将 Status
作为其 currentStatus
的人。
它可能知道如何检索所有在 status
集合中具有 Status
的人,但这是另一回事,不是吗? Hibernate Search 不知道 currentStatus
实际上是 status
属性 中包含的元素之一。就其所知,getCurrentStatus()
很可能会这样做:status.iterator().next().getParentStatus()
。那么当前的状态就不会被包含在Person#status
中,并且不清楚myStatus.getPatient()
是否可以return一个currentStatus
是myStatus
的Person
。
所以你需要明确地告诉 Hibernate Search:“从给定的 Status myStatus
,如果你检索 myStatus.getPatient()
的值,你会得到 Person
的 currentStatus
属性 可能指向 myStatus
”。这正是 @AssociationInverseSide
的用途。
我想将休眠搜索的 @IndexingDependency
与 PropertyBridge
一起使用,但我似乎无法让它工作。
我收到这个错误:
Hibernate ORM mapping: type 'com.something.Person': path '.currentStatus':
failures:
- HSEARCH700020: Unable to find the inverse side of the association on type
'com.something.Person' at path '.currentStatus<no value extractors>'. Hibernate Search
needs this information in order to reindex 'com.something.Person' when
'com.something.Status' is modified. You can solve this error by defining the inverse
side of this association, either with annotations specific to your integration
(@OneToMany(mappedBy = ...) in Hibernate ORM) or with the Hibernate Search
@AssociationInverseSide annotation. Alternatively, if you do not need to reindex
'com.something.Person' when 'com.something.Status' is modified, you can disable
automatic reindexing with @IndexingDependency(reindexOnUpdate = ReindexOnUpdate.SHALLOW)
不确定我是否做错了什么,或者我尝试做的事情是不可能的。感谢您的帮助。
这里是涉及到的文件。
Person.class
@Entity
@Table
@Indexed
public class Person {
@OneToMany(mappedBy = "patient", cascade = CascadeType.ALL)
private Set<Status> status = new HashSet<>();
@Transient
@StatusBinding(fieldName = "currentStatus")
@IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "status")))
public Status getCurrentStatus() {
return this.status.stream()
.filter(it -> it.getDate().isAfter(LocalDate.now()))
.max(Comparator.comparing(Status::getDate))
.orElse(null);
}
}
StatusBinding.class
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
@PropertyMapping(processor = @PropertyMappingAnnotationProcessorRef(type = StatusBinding.Processor.class))
@Documented
public @interface StatusBinding {
String fieldName() default "";
class Processor implements PropertyMappingAnnotationProcessor<StatusBinding> {
@Override
public void process(PropertyMappingStep mapping, StatusBindingannotation, PropertyMappingAnnotationProcessorContext context) {
StatusBinderbinder = new StatusBinder();
if (!annotation.fieldName().isBlank()) binder.setFieldName(annotation.fieldName());
mapping.binder(binder);
}
}
}
StatusBinder.class
public class StatusBinder implements PropertyBinder {
@Setter private String fieldName = "mainStatus";
@Override
public void bind(PropertyBindingContext context) {
context.dependencies()
.use("status")
.use("date")
.use("note");
IndexSchemaObjectField mainStatusField = context.indexSchemaElement().objectField(this.fieldName);
context.bridge(Status.class, new StatusBridge(
mainStatusField.toReference(),
mainStatusField.field("status", context.typeFactory().asString()).toReference(),
mainStatusField.field("date", context.typeFactory().asLocalDate()).toReference(),
mainStatusField.field("note", context.typeFactory().asString()).toReference()
));
}
private static class StatusBrige implements PropertyBridge<Status> {
private final IndexObjectFieldReference mainStatusField;
private final IndexFieldReference<String> statusField;
private final IndexFieldReference<LocalDate> dateField;
private final IndexFieldReference<String> noteField;
public StatusBrige(
IndexObjectFieldReference mainStatusField,
IndexFieldReference<String> statusField,
IndexFieldReference<LocalDate> dateField,
IndexFieldReference<String> noteField
) {
this.mainStatusField = mainStatusField;
this.statusField = statusField;
this.dateField = dateField;
this.noteField = noteField;
}
@Override
public void write(DocumentElement target, Status mainStatus, PropertyBridgeWriteContext context) {
DocumentElement statutElement = target.addObject(this.mainStatusField);
statutElement.addValue(this.statusField, mainStatus.getStatus);
statutElement.addValue(this.dateField, mainStatus.getDate());
statutElement.addValue(this.noteField, mainStatus.getNote());
}
}
}
问题
当 Status
实体被修改时,Hibernate Search 不知道如何检索具有 Status
作为其 currentStatus
的相应 Person
。
解决方案
假设currentStatus
总是包含在status
中,并且由于Status.patient
是Person.status
关联的反面,你应该只需要添加:
@Transient
@StatusBinding(fieldName = "currentStatus")
@IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "status")))
// ADD THIS ANNOTATION
@AssociationInverseSide(
inversePath = @ObjectPath(@PropertyValue(propertyName = "patient"))
)
public Status getCurrentStatus() {
// ...
}
为什么?
我会试着解释一下,但有点复杂,请耐心等待。
派生属性和关联的反面是相关概念:它们具有允许 Hibernate Search 执行自动重新索引的共同目的。
但是,它们仍然是不同的概念,Hibernate Search 无法从另一个推断出一个。
使用 @IndexingDependency(derivedFrom)
,您定义了 currentStatus
的计算依赖于:
@IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "status")))
public Status getCurrentStatus() {
这告诉 Hibernate Search,只要 status
属性 发生变化,currentStatus
就会发生变化。有了这些信息,Hibernate Search 就能够确定无论何时调用 person.getStatus().remove(...)
或 person.getStatus().add(...)
(例如),您的 Person
实体都需要重新索引,因为 currentStatus
已被索引,并且它可能改变了。
在您的自定义活页夹中,您也是 defining dependencies:
context.dependencies()
.use("status")
.use("date")
.use("note");
这告诉 Hibernate Search,每当 Status
实体的 status
、date
和 note
属性发生变化时,Person
具有 Status
因为它的 currentStatus
需要重建索引。
但是...Hibernate Search 不知道的是如何检索将 Status
作为其 currentStatus
的人。
它可能知道如何检索所有在 status
集合中具有 Status
的人,但这是另一回事,不是吗? Hibernate Search 不知道 currentStatus
实际上是 status
属性 中包含的元素之一。就其所知,getCurrentStatus()
很可能会这样做:status.iterator().next().getParentStatus()
。那么当前的状态就不会被包含在Person#status
中,并且不清楚myStatus.getPatient()
是否可以return一个currentStatus
是myStatus
的Person
。
所以你需要明确地告诉 Hibernate Search:“从给定的 Status myStatus
,如果你检索 myStatus.getPatient()
的值,你会得到 Person
的 currentStatus
属性 可能指向 myStatus
”。这正是 @AssociationInverseSide
的用途。