MappingException: Couldn't find PersistentEntity while implementing AbstractElasticsearchConfiguration

Viewed 1450

I want to implement custom converter for my object following this guide and this answer, but when I ran the project I received MappingException: Couldn't find PersistentEntity for type class TransactionElastic

Here is my config

@Configuration
public class ElasticConfig extends AbstractElasticsearchConfiguration {


    @Value("$elasticUrl")
    private String elasticUrl;

    @Override
    public RestHighLevelClient elasticsearchClient() {
        return RestClients.create(ClientConfiguration.create(elasticUrl)).rest();
    }

    @Bean
    public EntityMapper entityMapper() {
        ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(), new DefaultConversionService());
        entityMapper.setConversions(elasticsearchCustomConversions());
        return entityMapper;
    }

    @Bean
    @Override
    public ElasticsearchCustomConversions elasticsearchCustomConversions() {
        return new ElasticsearchCustomConversions(Arrays.asList(new TransactionToMapConverter(), new MapToTransactionConverter()));
    }
}

@WritingConverter
public class TransactionToMapConverter implements Converter<TransactionElastic, Map<String, Object>> {
    @Override
    public Map<String, Object> convert(TransactionElastic tradeTransaction) {
        Map<String, Object> target = new HashMap<>();
        target.put("id", tradeTransaction.getId());
        target.put("portfolio_name", tradeTransaction.getPortfolioName());
        target.put("settl_date", tradeTransaction.getSettlDate());
        return target;
    }
}
@ReadingConverter
public class MapToTransactionConverter implements Converter<Map<String, Object>, TransactionElastic> {
    @Override
    public TransactionElastic convert(Map<String, Object> map) {
        TransactionElastic t = new TransactionElastic();
        t.setPortfolioName(map.get("portfolio_name").toString());
        t.setId((Long) map.get("id"));
        return t;
    }
}

@Document(indexName = "transactions")
public class TransactionElastic {
    @Id
    private Long id;
    @Field(name = "portfolio_name", type = FieldType.Text)
    private String portfolioName;
    @Field(name = "settl_date", type = FieldType.Date)
    private Date settlDate;
    @Field(name = "trscn_status", type = FieldType.Text)
    private String trscnStatus;
    // getters and setters here 
}

Stacktrace

15:49:41.488 [restartedMain] ERROR o.s.boot.SpringApplication - Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'techNeedsController': Unsatisfied dependency expressed through field 'elasticService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'transactionServiceElastic': Unsatisfied dependency expressed through field 'transactionElasticRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionElasticRepository': Invocation of init method failed; nested exception is org.springframework.data.mapping.MappingException: Couldn't find PersistentEntity for type class com.portal.domain.TransactionElastic!
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:639)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:116)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:397)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1429)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:879)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
    at com.portal.Application.main(Application.java:17)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'transactionServiceElastic': Unsatisfied dependency expressed through field 'transactionElasticRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionElasticRepository': Invocation of init method failed; nested exception is org.springframework.data.mapping.MappingException: Couldn't find PersistentEntity for type class com.portal.domain.TransactionElastic!
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:639)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:116)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:397)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1429)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1287)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:636)
    ... 24 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionElasticRepository': Invocation of init method failed; nested exception is org.springframework.data.mapping.MappingException: Couldn't find PersistentEntity for type class com.portal.domain.TransactionElastic!
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1803)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:595)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1287)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:636)
    ... 37 common frames omitted
Caused by: org.springframework.data.mapping.MappingException: Couldn't find PersistentEntity for type class com.portal.domain.TransactionElastic!
    at org.springframework.data.mapping.context.MappingContext.getRequiredPersistentEntity(MappingContext.java:79)
    at org.springframework.data.elasticsearch.repository.support.ElasticsearchEntityInformationCreatorImpl.getEntityInformation(ElasticsearchEntityInformationCreatorImpl.java:49)
    at org.springframework.data.elasticsearch.repository.support.ElasticsearchRepositoryFactory.getEntityInformation(ElasticsearchRepositoryFactory.java:67)
    at org.springframework.data.elasticsearch.repository.support.ElasticsearchRepositoryFactory.getTargetRepository(ElasticsearchRepositoryFactory.java:72)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:312)
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$5(RepositoryFactoryBeanSupport.java:297)
    at org.springframework.data.util.Lazy.getNullable(Lazy.java:212)
    at org.springframework.data.util.Lazy.get(Lazy.java:94)
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:300)
    at org.springframework.data.elasticsearch.repository.support.ElasticsearchRepositoryFactoryBean.afterPropertiesSet(ElasticsearchRepositoryFactoryBean.java:67)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1862)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1799)
    ... 47 common frames omitted

Registering convertors logs

o.s.data.convert.CustomConversions - Registering converter from class com.portal.domain.TransactionElastic to interface java.util.Map as writing converter although it doesn't convert to a store-supported type! You might wanna check you annotation setup at the converter implementation.
o.s.data.convert.CustomConversions - Registering converter from interface java.util.Map to class com.portal.domain.TransactionElastic as reading converter although it doesn't convert from a store-supported type! You might wanna check you annotation setup at the converter implementation.
o.s.data.convert.CustomConversions - Registering converter from class org.springframework.data.geo.Point to interface java.util.Map as writing converter although it doesn't convert to a store-supported type! You might wanna check you annotation setup at the converter implementation.
o.s.data.convert.CustomConversions - Registering converter from interface java.util.Map to class org.springframework.data.geo.Point as reading converter although it doesn't convert from a store-supported type! You might wanna check you annotation setup at the converter implementation.
o.s.data.convert.CustomConversions - Registering converter from class org.springframework.data.elasticsearch.core.geo.GeoPoint to interface java.util.Map as writing converter although it doesn't convert to a store-supported type! You might wanna check you annotation setup at the converter implementation.
o.s.data.convert.CustomConversions - Registering converter from interface java.util.Map to class org.springframework.data.elasticsearch.core.geo.GeoPoint as reading converter although it doesn't convert from a store-supported type! You might wanna check you annotation setup at the converter implementation.
o.s.b.a.o.j.JpaBaseConfiguration$JpaWebConfiguration - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning

Spring boot version 2.2.1 Spring data elasticsearch 3.2.1 Elasticsearch version 6.4.0

What might cause the problem? Am I missing something?

2 Answers

I think that it's not able to find entities. Please extends the following method in AbstractElasticsearchConfiguration:

@Override
protected Collection<String> getMappingBasePackages() {
    //Provide list of your packages where you have entities.
}

Spring Boot Devtools

Are you using Spring Boot Devtools? DevTools can cause the MappingException error you are seeing because of its use of multiple classloaders.

One fix is to exclude your mapping package using the spring.devtools.additional-exclude property in your application.properties or application.yml like this:

spring.devtools.additional-exclude: static/**,com.portal.domain/**

A more drastic approach is to stop using devtools, but doing this will disable the LiveReload capability which is nice to have during development:

In Maven's pom.xml:

<!-- <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-devtools</artifactId>
 <optional>true</optional>
</dependency> -->

In Gradle's build.gradle:

/* compileOnly("org.springframework.boot:spring-boot-devtools") */

Devtools is only used in development and should not be disabled by default in production code. So if this is the error, your code should work when deployed in production.

The Cause

The cause of the error appears to be in this snippet of code in DefaultReactiveIndexOperations:

    private IndexCoordinates getIndexCoordinatesFor(Class<?> clazz) {
        return operations.getElasticsearchConverter().getMappingContext().getRequiredPersistentEntity(clazz)
                .getIndexCoordinates();
    }

The call to getRequiredPersisentEntity(clazz) fails because it looks up clazz in a map of class -> PersistentEntities, and the class is different because of the separate class loader.

I do not know why the mapping class is loaded in separate class loaders.

Related