irpas技术客

Elastic实战:彻底解决spring-data-elasticsearch日期、时间类型数据读取报错问题_wu_55555

irpas 6338

0. 引言

在使用spring-data-elasticsearch读取es中时间类型的数据时出现了日期转换报错,不少初学者会在这里困惑很久,所以今天我们专门来解读该问题的几种解决方案。

1. 问题分析

该问题的报错形式一般是:

Failed to convert from type [java.lang.String] to type [java.util.Date] for value '2022-03-15T14:31:55+08:00'; nested exception is java.lang.IllegalArgumentException"

当然时间类型的表现形式不一定是我这里的2022-03-15T14:31:55+08:00,可能多种多样,但解决办法都是一致的。

该问题的原因很简单,就是es中存储的时间格式是该种类型的,使用java client去获取时,无法直接转换为时间类型

2. 问题解决

这里演示的环境版本是如下所示。如果在实操时发现部分代码不可用,注意检查版本

spring-data-elasticsearch3.2.12.RELEASE 2.1 es mapping与实体类中声明相同的时间格式

第一种方案,是我们应该在实体类和索引mapping创建前就做好的,将es mapping中的时间字段的格式与实体类中保持统一 1、es mapping中设置时间格式,如果有多种格式中间用||隔开

PUT date_custom { "mappings": { "properties": { "create_time": { "type": "date", "format": "yyyy-MM-dd HH:mm:ss || yyyy-MM-dd'T'HH:mm:ss'+08:00' || strict_date_optional_time || epoch_millis" } } } }

2、实体类中同样声明该时间格式

@Data @Document(indexName = "date_custom",shards = 1, replicas = 0) public class DateTest implements Serializable { @Field(type = FieldType.Date, name = "create_time",format = {}, pattern = "yyyy-MM-dd HH:mm:ss || yyyy-MM-dd'T'HH:mm:ss'+08:00' || strict_date_optional_time || epoch_millis") private Date createTime; // 旧版本 // @Field(type = FieldType.Date, name = "create_time",format = DateFormat.custom, pattern = "yyyy-MM-dd HH:mm:ss || yyyy-MM-dd'T'HH:mm:ss'+08:00' || strict_date_optional_time || epoch_millis") // private Date createTime; }

es自带的时间格式,可以在官方文档中找到

2.2 配置日期格式转换器

1、ElasticsearchRestTemplate的构造方法中提供了一个构造方法,可以传入指定的EntityMapper

@Bean public ElasticsearchRestTemplate elasticsearchRestTemplate(RestHighLevelClient elasticsearchClient,EntityMapper entityMapper){ return new ElasticsearchRestTemplate(elasticsearchClient,entityMapper); }

2、EntityMapper有两个实现类,其中ElasticsearchEntityMapper实现类提供了一个自定义转换器的方法setConversions

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

3、通过该方法我们可以将我们自定义的转换器StringToDateConverter传入进去

@Override public ElasticsearchCustomConversions elasticsearchCustomConversions(){ List<Converter<?,?>> converterList = Lists.newArrayList(StringToDateConverter.INSTANT); return new ElasticsearchCustomConversions(converterList); }

4、完整代码

import com.google.common.collect.Lists; import org.elasticsearch.client.RestHighLevelClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.converter.Converter; import org.springframework.data.elasticsearch.client.ClientConfiguration; import org.springframework.data.elasticsearch.client.RestClients; import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration; import org.springframework.data.elasticsearch.core.ElasticsearchEntityMapper; import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; import org.springframework.data.elasticsearch.core.EntityMapper; import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions; import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories; import org.springframework.http.HttpHeaders; import org.springframework.lang.NonNull; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; @Configuration @EnableElasticsearchRepositories(basePackages = "com.example.datedemo") public class ElasticRestClientConfig extends AbstractElasticsearchConfiguration { @Value("${spring.elasticsearch.rest.uris}") private String url; @Value("${spring.elasticsearch.rest.username}") private String username; @Value("${spring.elasticsearch.rest.password}") private String password; @Override @Bean public RestHighLevelClient elasticsearchClient() { url = url.replace("http://",""); HttpHeaders headers = new HttpHeaders(); headers.setBasicAuth(username,password); final ClientConfiguration clientConfiguration = ClientConfiguration.builder() .connectedTo(url) .withDefaultHeaders(headers) .build(); return RestClients.create(clientConfiguration).rest(); } @Bean public ElasticsearchRestTemplate elasticsearchRestTemplate(RestHighLevelClient elasticsearchClient,EntityMapper entityMapper){ return new ElasticsearchRestTemplate(elasticsearchClient,entityMapper); } /** * 指定EntityMapper为ElasticsearchEntityMapper * 解决es mapper映射实体类问题 * @return */ @Bean @Override public EntityMapper entityMapper() { ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(), new DefaultConversionService()); entityMapper.setConversions(elasticsearchCustomConversions()); return entityMapper; } /** * 指定日期转换器,解决日期转换错误问题 * @return */ @Override public ElasticsearchCustomConversions elasticsearchCustomConversions(){ List<Converter<?,?>> converterList = Lists.newArrayList(StringToDateConverter.INSTANT); return new ElasticsearchCustomConversions(converterList); } /** * 字符串转换日期 */ private enum StringToDateConverter implements Converter<String, java.util.Date> { /** * 转换器实例 */ INSTANT; @Override public Date convert(@NonNull String source) { try { return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'+08:00'").parse(source); } catch (ParseException e) { e.printStackTrace(); } return null; } } } 2.3 配置文件设置spring.jackson.date-format

配置文件中设置时间类型格式,但这种方式全局生效,小心使用

spring: jackson: date-format: yyyy-MM-dd HH:mm:ss 2.4 单独创建日期类型转换类 @Component public class DateConverter implements Converter<String, Date> { @Override public Date convert(@NonNull String source) { try { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(source); } catch (ParseException e) { try { return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'+08:00'").parse(source); } catch (ParseException ex) { try { return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").parse(source); } catch (ParseException ex2) { ex2.printStackTrace(); } } } return null; } } 2.5 @DateTimeFormat注解声明格式(不生效)

首先说明一下这种方式并不生效,这里单独说明是为了列举出来,让大家避免走弯路。

该种方法的原理是通过在注解中声明自定义格式来实现格式转换,但实际上这并不起作用,因为spring-data-elasticsearch会忽略@DateTimeFormat注解

@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") //返回时间类型 @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") //接收时间类型 private Date startTime;


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #1 #To #convert #from