【开发心得】Spring data elastic search 解决Long转Timestamp时间问题。No converter found capable of converting from
  OIW0KlaMcRRl 2023年11月28日 14 0


前言:

以下解决方案均基于Spring boot 2.x Spring data elastic 3/4 以及 elastic search high level client 7.8。

报错:

No converter found capable of converting from type [java.lang.Long] to type [java.sql.Timestamp]

2021/09/01 更新(发现了一个更稳妥的解决方案)

解决方案0:

 参考:

在配置类中增加 elasticsearchConverter

@Bean
    ElasticsearchConverter elasticsearchConverter(SimpleElasticsearchMappingContext mappingContext) {
        DefaultConversionService defaultConversionService = new DefaultConversionService();
        defaultConversionService.addConverter(DateToLocalDateTimeConverter.INSTANCE);
        defaultConversionService.addConverter(StringToLocalDateTimeConverter.INSTANCE);
        defaultConversionService.addConverter(LongToLocalDateTimeConverter.INSTANCE);
        defaultConversionService.addConverter(LongToTimestampConverter.INSTANCE);
        defaultConversionService.addConverter(TimestampToLongConverter.INSTANCE);
        return new MappingElasticsearchConverter(mappingContext, defaultConversionService);
    }

 然后注入 SimpleElasticsearchMappingContext

在新建esTemplate的时候,使用两个参数的构造器即可,贴一下构造器源码

public ElasticsearchRestTemplate(RestHighLevelClient client, ElasticsearchConverter elasticsearchConverter) {
        Assert.notNull(client, "Client must not be null!");
        this.client = client;
        this.exceptionTranslator = new ElasticsearchExceptionTranslator();
        this.initialize(elasticsearchConverter);
    }

解决方案1:

Spring data elastic search 版本 3.x,注意到4.x实际上没有ElasticsearchEntityMapper,全网搜到比较多的解决方案是基于这个版本。

@Configuration
public class ESConfiguration extends AbstractElasticsearchConfiguration {

    @Value("${elasticsearch.config.hosts}")
    private String hosts;

    @Bean
    public RestHighLevelClient elasticsearchClient() {
        ClientConfiguration configuration = ClientConfiguration.builder()
                .connectedTo(this.hosts)
                .build();

        return RestClients.create(configuration).rest();
    }

    @Bean
    @Override
    public EntityMapper entityMapper() {

        ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(
                elasticsearchMappingContext(), new DefaultConversionService()
        );
        entityMapper.setConversions(elasticsearchCustomConversions());

        return entityMapper;
    }

    /**
     * 默认的converter不支持long到localdatime的转换,从elasticsearch读取的时候会报错,所以在这里添加一个。
     */
    @Bean
    @Override
    public ElasticsearchCustomConversions elasticsearchCustomConversions() {
        List<Converter> converters= new ArrayList<>();
        converters.add(LongToLocalDateTimeConverter.INSTANCE);
        return new ElasticsearchCustomConversions(converters);
    }

    @ReadingConverter
    static enum LongToLocalDateTimeConverter implements Converter<Long, LocalDateTime> {
        INSTANCE;

        private LongToLocalDateTimeConverter() {
        }

        public LocalDateTime convert(Long source) {
            return Instant.ofEpochMilli(source).atZone(ZoneId.systemDefault()).toLocalDateTime();
        }
    }
}

解决方案2:

基于 spring data elastic search 4.x

ConfigurationProperties(prefix = "elasticsearch") // 读取elasticsearch开头的配置
@Configuration
@Data
public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {
    private String host;
    private Integer port;

    // 重写父类方法构建参数
    @Override
    public RestHighLevelClient elasticsearchClient() {
        RestClientBuilder builder = RestClient.builder(new HttpHost(host, port));
        RestHighLevelClient restHighLevelClient = new RestHighLevelClient(builder);
        return restHighLevelClient;
    }


    @Bean
    public ElasticsearchRestTemplate esRestTemplate() throws Exception {
        return new ElasticsearchRestTemplate(elasticsearchClient());
    }
    @Bean
    @Override
    public ElasticsearchCustomConversions elasticsearchCustomConversions() {
        List<Converter> converters= new ArrayList<>();
        converters.add(DateToLocalDateTimeConverter.INSTANCE);
        converters.add(StringToLocalDateTimeConverter.INSTANCE);
        converters.add(LongToLocalDateTimeConverter.INSTANCE);
        converters.add(LongToTimestampConverter.INSTANCE);
        converters.add(TimestampToLongConverter.INSTANCE);
        return new ElasticsearchCustomConversions(converters);
    }


    //格式化后保存结果为String类型
    @ReadingConverter
    enum StringToLocalDateTimeConverter implements Converter<String, LocalDateTime> {

        INSTANCE;

        @Override
        public java.time.LocalDateTime convert(String source) {
            DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            return LocalDateTime.parse(source,df);
        }

    }

    @WritingConverter
    enum DateToLocalDateTimeConverter implements Converter<Date, LocalDateTime> {

        INSTANCE;

        @Override
        public LocalDateTime convert(Date date) {
            Instant instant = date.toInstant();
            return instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
        }
    }
    //保存类型为long类型
    @ReadingConverter
    enum LongToLocalDateTimeConverter implements Converter<Long, LocalDateTime> {

        INSTANCE;

        @Override
        public java.time.LocalDateTime convert(Long source) {
            return Instant.ofEpochMilli(source).atZone(ZoneId.systemDefault()).toLocalDateTime();
        }

    }
    @ReadingConverter
    enum LongToTimestampConverter implements Converter<Long, Timestamp> {
        INSTANCE;

        @Override
        public java.sql.Timestamp convert(Long longTime) {
            if(longTime != null){
                return new Timestamp(longTime);
            }
            return null;
        }
    }
    @ReadingConverter
    enum TimestampToLongConverter implements Converter<Timestamp,Long> {
        INSTANCE;

        @Override
        public Long convert(Timestamp source) {
            return source.getTime();
        }
    }

    // 4版本无法使用使用
//    @Bean
//    @Override
//    public EntityMapper entityMapper() {
//
//        ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(
//                elasticsearchMappingContext(), new DefaultConversionService()
//        );
//        entityMapper.setConversions(elasticsearchCustomConversions());
//
//        return entityMapper;
//    }

    /**
     * 默认的converter不支持
     * long到localdatime的转换,
     * long到TimeStamp的转换,
     * 从elasticsearch读取的时候会报错,所以在这里添加一个。
     */
//    @Bean
//    @Override
//    public ElasticsearchCustomConversions elasticsearchCustomConversions() {
//        List<Converter> converters= new ArrayList<>();
//        converters.add(LongToLocalDateTimeConverter.INSTANCE);
//        converters.add(LongToTimeStampConverter.INSTANCE);
//        return new ElasticsearchCustomConversions(converters);
//    }
//
//    @ReadingConverter
//    @WritingConverter
//    static enum LongToLocalDateTimeConverter implements Converter<Long, LocalDateTime> {
//        INSTANCE;
//
//        private LongToLocalDateTimeConverter() {
//        }
//
//        public LocalDateTime convert(Long source) {
//            return Instant.ofEpochMilli(source).atZone(ZoneId.systemDefault()).toLocalDateTime();
//        }
//    }
//    @ReadingConverter
//    @WritingConverter
//    static enum LongToTimeStampConverter implements Converter<Long, Timestamp> {
//        INSTANCE;
//        private LongToTimeStampConverter(){
//
//        }
//
//        @Override
//        public Timestamp convert(Long longTime) {
//            if(longTime != null){
//                return new Timestamp(longTime);
//            }
//            return null;
//        }
//    }
}

解决方案3:

在存的时候就将数据格式化在存,不要存long <=> timestamp 本身就算读取到还得格式化,为啥不存的时候格式化好呢。

补充:

如果提示重复的Bean注入,看下这里,很多帖子都说custom converter的时候,会集成AbstractElasticsearchConfiguration,但是如果和你的high level rest template的配置在一起的话,看下冲突。

【开发心得】Spring data elastic search 解决Long转Timestamp时间问题。No converter found capable of converting from_Elastic

结语:

耗时比较长去解决这个小问题,最终是用方案3解决的,方案2遇到的问题是,converter注入了,但是在使用的时候,没有找到,真的有点浪费了。Spring data elastic search issue里边没有提到这个问题,目前也不知道4.1.x的版本是不是解决了,我的版本基于spring data elastic search 4.0.9.RELEASE,stack over flow 上有人提到这个问题,但是最后也是修改的格式,不晓得有没有其他方案,如果有路过的大佬,请指教。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月28日 0

暂无评论

推荐阅读
OIW0KlaMcRRl