首页 > 其他分享 >Elastic实战:彻底解决spring-data-elasticsearch日期、时间类型数据读取报错问题

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

时间:2024-01-16 18:11:07浏览次数:30  
标签:Elastic spring dd springframework yyyy 报错 elasticsearch import org

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;

 

================
1、my es mapping

PUT test001
{
  "mappings": {
    "properties": {
      "order_count": {
        "type": "long"
      },
      "pay_amount_sum": {
        "type": "double"
      },
      "shop_id": {
        "type": "long"
      },
      "statistic_time": {
        "type": "date",
         "format": "yyyy-MM-dd HH:mm:ss || strict_date_optional_time || epoch_millis"
      }
    }
  }
}

 

2、my java model code 

   @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @Field(value = "create_time", type = FieldType.Date, format = DateFormat.epoch_millis)
    private Date createTime;

 

3、my exception 

Unable to convert value '2022-05-23 14:49:24' to java.util.Date for property 'createTime'

4、my change

 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @Field(value = "create_time", type = FieldType.Date, format = {},
            pattern = "yyyy-MM-dd HH:mm:ss || strict_date_optional_time || epoch_millis")
    private Date createTime;

 

标签:Elastic,spring,dd,springframework,yyyy,报错,elasticsearch,import,org
From: https://www.cnblogs.com/hbuuid/p/16386907.html

相关文章

  • Spring Boot3.x集成ElasticSearch8.x
    SpringBoot3.x集成ElasticSearch8.x版本说明,本demo使用SpringBoot3.2.1+JDK17+ElasticSearch8.11.3前提是已经部署好了自己的ElasticSearch环境,我这里直接用容器默认部署好了,能访问即可创建SpringBoot项目导入pom依赖<dependency><grou......
  • Spring事务传播机制
    1.Spring对事物的支持一般有两种方式编程式事务管理:通过 TransactionTemplate或者TransactionManager手动管理事务,实际应用中很少使用,这不是本文的重点,就不在这里赘述。声明式事务管理:使用场景最多,也是最推荐使用的方式,直接加上@Transactional注解即可。2.Transactional注......
  • CentOS7 报错 ”Repository base is listed more than once in the configuration...
    CentOS7在使用yum时出现以下错误:RepositorybaseislistedmorethanonceintheconfigurationRepositoryupdatesislistedmorethanonceintheconfigurationRepositoryextrasislistedmorethanonceintheconfigurationRepositorycentosplusislistedmore......
  • springboot~shardingsphere在非spring框架中的使用
    shardingsphere已经很方便的被springboot集成了,你只要引入sharding-jdbc-spring-boot-starter这个包就可以了,而如果是原生java的话,你就需要自己去实现了,主要是重新定义数据源,定义规则等问题,本文主要介绍原生环境下的shardingsphere的使用。依赖引用<dependencies><!--......
  • [spring] spring学习笔记(2): 通过xml实现依赖注入 - 特殊注入类型
    实际应用中,我们的对象可能会引用很多不同类型的东西,不单单只是几个数值对象类型在前一篇文章中,已经使用引用对象作为例子,关键在于使用ref<!--注意引用的对象要先创建Bean,id为weapon1--><beanid="player1"class="com.demo.player"> <!--通过setter注入,注意ref的......
  • springboot第48集:【思维导图】地图,面向对象,异常,功能代码
    在SpringBoot中,可以通过编写拦截器(Interceptor)来对请求进行拦截与处理。下面是一个简单的拦截器实现示例:创建一个类并实现HandlerInterceptor接口publicclassAuthInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequest......
  • Springboot上传文件大小限制处理
    今天在开发过程中遇到一个文件上传的问题io.undertow.server.RequestTooBigException:UT000020:Connectionterminatedasrequestwaslargerthan10485760Servlet容器使用的是undertow,看异常信息应该是默认存在10MB的文件大小限制。百度了一下,找到如下配置,问题得以解决,记......
  • Spring Cloud整体架构解析
    SpringCloud整体架构SpringCloud的中文名我们就暂且称呼它为“春云”吧,听上去是多么朴实无华的名字,不过呢一般名字起的低调的都是厉害角色,我们就看看SpringCloud都提供了哪些靠谱功能吧。SpringCloud是一款微服务架构的一站式解决方案,你在微服务化过程中碰到的任何问题,都可......
  • SpringSecurity表单认证(二)
    用户名+密码系统默认登录用户名:user密码每次服务启动后随机生成密码用户信息获取原理(数据库获取)实现该接口,security默认自动生成密码关闭。框架源码:packageorg.springframework.security.core.userdetails;publicinterfaceUserDetailsService{UserDetailsloa......
  • 安装torch2trt中遇到的报错:ModuleNotFoundError: No module named 'torch2trt.flatten
    这一个报错折腾了一整天,在这里记录一下方便他人可以快速解决问题:首先说明一下本人的版本情况:Linux系统CUDA:11.8Python:3.8torch:2.0.0torchvision:0.15.0代码中报错的位置是:fromtorch2trtimporttorch2trt报错的原因是:ModuleNotFoundError:Nomodulenamed'torch2trt......