Elastic实战:彻底解决spring-data-elasticsearch日期时间类型数据读取报错问题
Posted wu_55555
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Elastic实战:彻底解决spring-data-elasticsearch日期时间类型数据读取报错问题相关的知识,希望对你有一定的参考价值。
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 @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;
开发者涨薪指南
48位大咖的思考法则、工作方式、逻辑体系
以上是关于Elastic实战:彻底解决spring-data-elasticsearch日期时间类型数据读取报错问题的主要内容,如果未能解决你的问题,请参考以下文章
AI 实战篇 |基于 AI开放平台实现 货币识别 功能,彻底解决货币盲区
AI 实战篇 |基于 AI开放平台实现 货币识别 功能,彻底解决货币盲区
Elastic认证特训营 难点解读04——集群问题排查实战指南
大咖直播Elastic Security 安全管理实战工作坊(第二期)