实现Elasticsearch自定义转换器时遇MappingException问题求助
最近我在给自定义的TransactionElastic对象实现Elasticsearch转换器时,启动项目直接报错了——抛出MappingException: Couldn't find PersistentEntity for type class TransactionElastic,同时转换器注册日志还提示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!。下面是我当时的相关代码和环境信息,折腾一番后终于找到问题根源,分享给大家:
我的配置与代码
配置类
@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 已省略 }
错误堆栈日志
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! ...(省略重复堆栈内容)
转换器注册警告日志
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. ...(省略其他转换器日志)
环境版本
- Spring Boot版本:2.2.1
- Spring Data Elasticsearch版本:3.2.1
- Elasticsearch版本:6.4.0
问题根源分析
- 实体类未被Spring Data扫描识别:虽然给
TransactionElastic加了@Document注解,但如果实体所在包不在Spring的自动扫描范围内,Spring Data Elasticsearch无法加载该实体的元数据,自然找不到对应的PersistentEntity。 - 自定义EntityMapper破坏默认映射上下文:手动实现的
EntityMapper没有正确关联到Spring Data的Elasticsearch映射上下文,导致实体的映射规则无法被正确加载。 - 转换器注册依赖实体元数据:因为实体未被识别,转换器注册时无法匹配到对应的存储类型,进而触发警告并引发后续的映射异常。
解决方案
1. 明确指定扫描范围
在配置类或启动类上添加注解,明确指定Repository和实体类的扫描路径,避免Spring漏扫:
@Configuration @EnableElasticsearchRepositories(basePackages = "com.portal.repository") // 替换为你的Repository所在包 @EntityScan(basePackages = "com.portal.domain") // 替换为你的实体类所在包 public class ElasticConfig extends AbstractElasticsearchConfiguration { @Value("${elasticUrl}") private String elasticUrl; @Override public RestHighLevelClient elasticsearchClient() { return RestClients.create(ClientConfiguration.create(elasticUrl)).rest(); } @Override public ElasticsearchCustomConversions elasticsearchCustomConversions() { return new ElasticsearchCustomConversions(Arrays.asList(new TransactionToMapConverter(), new MapToTransactionConverter())); } }
2. 移除自定义的EntityMapper
AbstractElasticsearchConfiguration已经提供了默认的EntityMapper实现,手动定义的Bean会破坏默认的映射上下文关联,直接删除entityMapper()方法即可。
3. 验证Repository接口正确性
确保你的Repository接口正确继承ElasticsearchRepository,泛型参数匹配实体类和主键类型:
public interface TransactionElasticRepository extends ElasticsearchRepository<TransactionElastic, Long> { // 自定义查询方法按需添加 }
4. 检查转换器注解
确认转换器上的@WritingConverter和@ReadingConverter来自org.springframework.data.convert包,避免导入错误的注解。
做完以上调整后,重启项目,MappingException和转换器警告都会消失,实体能被正常识别,转换器也可以正常工作。
内容的提问来源于stack exchange,提问作者vchr

