Skip to content
Advertisement

Spring data elasticsearch: Using @Document annotation on POJO interface class not working

I am migrating an application to the latest spring-boot version (using maven spring-boot-dependencies with version 2.5.4).

I have an interface called Customer and I have two implementations of that interface (BusinessCustomer, PrivateCustomer)

The three classes are annotated like that:

@Document(indexName = "customers", type = "customer")
@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.EXISTING_PROPERTY,
        property = "customerType"
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = BusinessCustomer.class, name = "BUSINESS_CUSTOMER"),
        @JsonSubTypes.Type(value = PrivateCustomer.class, name = "PRIVATE_CUSTOMER")
})
public interface Customer {
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.EXISTING_PROPERTY,
        property = "customerType",
        defaultImpl = BusinessCustomer.class
)
@Entity
@Document(indexName = "customers", type = "customer")
public class BusinessCustomer implements Serializable, Customer, Cloneable {
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.EXISTING_PROPERTY,
        property = "customerType",
        defaultImpl = PrivateCustomer.class
)
@Entity
@Document(indexName = "customers", type = "customer")
public class PrivateCustomer implements Serializable, Customer, Cloneable {

For querying the index “customers” I used to have code like:

elasticsearchOperations.count(query, Customer.class);

But this is not working anymore. I get an error at runtime:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.data.mapping.model.MappingInstantiationException: Failed to instantiate Customer using constructor NO_CONSTRUCTOR with arguments ] with root cause org.springframework.beans.BeanInstantiationException: Failed to instantiate [Customer]: Specified class is an interface

Is it not possible anymore to have two different classes in the same index like this? Has this to be done now with ElasticsearchCustomConversions somehow?

Advertisement

Answer

The MappingElasticsearchConverter in Spring Data Elasticsearch does not work on interfaces but relies on the @Document and @Field annotations that are set on an entity class.

All Spring Data modules – not only Spring Data Elasticsearch – use the concept of a PersistentEntity which is the metainformation about the class to store. And PersistentPropertys, this is the metainformation about the properties of the class. For Spring Data Elasticsearch this is information about the field name in Elasticsearch, it’s type, information like date formats etc.

A @Document annotated class is associated with an index. Since version 7 Elasticsearch does not support having multiple types in one index anymore.

So you should store your different entities in two indices:

@Document(indexName="business-customers")
class BusinessCustomer {
    // ...
}

@Document(indexName="private-customers")
class PrivateCustomer {
    // ...
}

Searching from these two indexes in one call and have the different data returned is possible, I wrote a blog post about how this can be done (https://www.sothawo.com/2021/05/reading-different-entities-from-multiple-indices-with-one-call-using-spring-data-elasticsearch/).

User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement