springboot ElasticSearch如何配置自定义转换器ElasticsearchCustomConversions
作者:子黄求知若渴
问题场景
在将Timestamp类型存进Elasticsearc后,将其取出时抛出异常。
从提示中可以看出缺少从Long转换到Timestamp的类型转换器。
原理及分析
在spring-data-elasticsearch中,java对象到json的互相转换是通过ElasticsearchConverter来进行的。
使用springboot的自动配置机制能够快速地完成elasticsearch的配置,导入spring-boot-starter-data-elasticsearch依赖后,spring将会自动向ioc容器中添加client,converter,template等Bean,只需要简单地配置elasticSearch服务器信息后就可以使用。
在org.springframework.boot.autoconfigure.data.elasticsearch中通过@Import导入ElasticsearchDataConfiguration类,默认的converter就是通过这个类提供的。
//org.springframework.boot.autoconfigure.data.elasticsearch @Import({ ElasticsearchDataConfiguration.BaseConfiguration.class, ElasticsearchDataConfiguration.RestClientConfiguration.class, ElasticsearchDataConfiguration.ReactiveRestClientConfiguration.class }) public class ElasticsearchDataAutoConfiguration { }
在org.springframework.boot.autoconfigure.data.elasticsearch中可以看到方法elasticsearchConverter,使用@Bean将其注册到ioc容器中,@ConditionalOnMissingBean则是当容器中没有相同类型的Bean才会进行创建。
//org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataConfiguration.BaseConfiguration @Bean @ConditionalOnMissingBean ElasticsearchConverter elasticsearchConverter(SimpleElasticsearchMappingContext mappingContext) { return new MappingElasticsearchConverter(mappingContext); }
这里使用了MappingElasticsearchConverter来作为elasticsearch的默认类型转换器。
通过其源码可以看出MappingElasticsearchConverter提供了两种构造方法,自动配置类使用的是第一种,其conversionService为空,MappingElasticsearchConverter会使用类DefaultConversionService对属性conversions来进行初始化,提供基础的转换功能。
public MappingElasticsearchConverter( MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext) { this(mappingContext, null); } public MappingElasticsearchConverter( MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext, @Nullable GenericConversionService conversionService) { Assert.notNull(mappingContext, "MappingContext must not be null!"); this.mappingContext = mappingContext; this.conversionService = conversionService != null ? conversionService : new DefaultConversionService(); this.typeMapper = ElasticsearchTypeMapper.create(mappingContext); }
同时MappingElasticsearchConverter类提供了setConversions来设置自定义的转换器CustomConversions。
通过向conversions添加自定义的converter来添加。
/** * Set the {@link CustomConversions} to be applied during the mapping process. <br /> * Conversions are registered after {@link #afterPropertiesSet() bean initialization}. * * @param conversions must not be {@literal null}. */ public void setConversions(CustomConversions conversions) { this.conversions = conversions; }
这种方法来自于spring-data官方文档 章节6.1.3
使用方法
由于@ConditionalOnMissingBean的存在,我们只需要自己创建一个ElasticsearchConverter并添加到环境中既可。
首先先创建两个Converter,分别用于Timestamp, Long的互相转换。
@WritingConverter static class TimestampToLong implements Converter<Timestamp, Long> { @Override public Long convert(Timestamp source) { return source.getTime(); } } @ReadingConverter static class LongToTimestamp implements Converter<Long, Timestamp> { @Override public Timestamp convert(Long source) { return new Timestamp(source); } }
方法1:
观察MappingElasticsearchConverter的构造方法发现,可以自行传入ConversionService来初始化。
这里使用了defaultConversionService,避免其他的类型转换受到影响,向其中添加自定义的两个converter, 并将其添加到MappingElasticsearchConverter构造器参数中。
@Bean ElasticsearchConverter elasticsearchConverter(SimpleElasticsearchMappingContext mappingContext) { DefaultConversionService defaultConversionService = new DefaultConversionService(); defaultConversionService.addConverter(new TimestampToLong()); defaultConversionService.addConverter(new LongToTimestamp()); return new MappingElasticsearchConverter(mappingContext, defaultConversionService); }
方法2:
使用MappingElasticsearchConverter.setConversions方法向其添加自定义转换服务ElasticsearchCustomConversions。
首先创建一个ElasticsearchCustomConversions,添加两个自定义的转换器。
随后在elasticsearchConverter中通过setConversions添加到MappingElasticsearchConverter中。
@Bean public ElasticsearchCustomConversions elasticsearchCustomConversions() { return new ElasticsearchCustomConversions( Arrays.asList(new TimestampToLong(), new LongToTimestamp())); } @Bean public ElasticsearchConverter elasticsearchConverter(SimpleElasticsearchMappingContext mappingContext, ElasticsearchCustomConversions elasticsearchCustomConversions) { MappingElasticsearchConverter mappingElasticsearchConverter = new MappingElasticsearchConverter(mappingContext); mappingElasticsearchConverter.setConversions(elasticsearchCustomConversions); return mappingElasticsearchConverter; }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。