首页 > 解决方案 > 使用 @IndexingDependency derivedFrom 和属性桥

问题描述

我想将休眠搜索@IndexingDependency与 a一起使用,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)

不知道我做错了什么,或者我想做的事情是不可能的。感谢您的帮助。

以下是涉及的文件。

人物类

@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);
    }
}

状态绑定.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());
        }
    }
}

标签: hibernate-searchhibernate-search-6

解决方案


问题

当一个Status实体被修改时,Hibernate Search 不知道如何检索相应Person的将其Status作为其currentStatus.

解决方案

假设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 SearchcurrentStatus将在status属性更改时更改。有了这些信息,Hibernate Search 就能够确定无论何时您调用person.getStatus().remove(...)person.getStatus().add(...)(例如)您的Person实体需要重新索引,因为currentStatus已编入索引,并且它可能已更改。

在您的自定义活页夹中,您还定义了依赖项

        context.dependencies()
            .use("status")
            .use("date")
            .use("note");

这告诉 Hibernate Search,每当实体的 、 和 属性发生变化时statusdatenote需要重新索引。StatusPersonStatuscurrentStatus

但是...... Hibernate Search 不知道的是如何检索将其Status作为currentStatus.

它可能知道如何检索所有Status在他们的status集合中拥有它的人,但这是另一回事,不是吗?Hibernate Search 不知道这currentStatus实际上是status属性中包含的元素之一。据它所知,getCurrentStatus()很可能会这样做:status.iterator().next().getParentStatus(). 那么当前状态不会包含在 中Person#status,并且不清楚是否myStatus.getPatient()可以返回Personwho currentStatusis myStatus

因此,您需要明确告诉 Hibernate Search:“从给定的Status myStatus中,如果您检索 的值myStatus.getPatient(),您将获得PersoncurrentStatus属性可能指向的myStatus”。这正是@AssociationInverseSide它的用途。


推荐阅读