Es对Date类型异常的处理 Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException

  当我们想要存储一个业务bean的时候,一般都会添加createTime,updateTime字段。如果使用数据库,我们一般会将时间字段设计为Date类型,但是如果是使用es,倘若不了解其底层机制,那么会遇到一些意向不到的问题。

   我们先看一下es是如何对Date类型处理的,代码如下:

private void writeValue(Object value) throws IOException {
        if (value == null) {
            generator.writeNull();
            return;
        }
        Class type = value.getClass();
        if (type == String.class) {
            generator.writeString((String) value);
        } else if (type == Integer.class) {
            generator.writeNumber(((Integer) value).intValue());
        } else if (type == Long.class) {
            generator.writeNumber(((Long) value).longValue());
        } else if (type == Float.class) {
            generator.writeNumber(((Float) value).floatValue());
        } else if (type == Double.class) {
            generator.writeNumber(((Double) value).doubleValue());
        } else if (type == Byte.class) {
            generator.writeNumber(((Byte)value).byteValue());
        } else if (type == Short.class) {
            generator.writeNumber(((Short) value).shortValue());
        } else if (type == Boolean.class) {
            generator.writeBoolean(((Boolean) value).booleanValue());
        } else if (type == GeoPoint.class) {
            generator.writeStartObject();
            generator.writeNumberField("lat", ((GeoPoint) value).lat());
            generator.writeNumberField("lon", ((GeoPoint) value).lon());
            generator.writeEndObject();
        } else if (value instanceof Map) {
            writeMap((Map) value);
        } else if (value instanceof Iterable) {
            generator.writeStartArray();
            for (Object v : (Iterable) value) {
                writeValue(v);
            }
            generator.writeEndArray();
        } else if (value instanceof Object[]) {
            generator.writeStartArray();
            for (Object v : (Object[]) value) {
                writeValue(v);
            }
            generator.writeEndArray();
        } else if (type == byte[].class) {
            generator.writeBinary((byte[]) value);
        } else if (value instanceof Date) {
            generator.writeString(XContentBuilder.defaultDatePrinter.print(((Date) value).getTime()));
        } else if (value instanceof Calendar) {
            generator.writeString(XContentBuilder.defaultDatePrinter.print((((Calendar) value)).getTimeInMillis()));
        } else if (value instanceof ReadableInstant) {
            generator.writeString(XContentBuilder.defaultDatePrinter.print((((ReadableInstant) value)).getMillis()));
        } else if (value instanceof BytesReference) {
            BytesReference bytes = (BytesReference) value;
            if (!bytes.hasArray()) {
                bytes = bytes.toBytesArray();
            }
            generator.writeBinary(bytes.array(), bytes.arrayOffset(), bytes.length());
        } else if (value instanceof BytesRef) {
            BytesRef bytes = (BytesRef) value;
            generator.writeBinary(bytes.bytes, bytes.offset, bytes.length);
        } else if (value instanceof Text) {
            Text text = (Text) value;
            if (text.hasBytes() && text.bytes().hasArray()) {
                generator.writeUTF8String(text.bytes().array(), text.bytes().arrayOffset(), text.bytes().length());
            } else if (text.hasString()) {
                generator.writeString(text.string());
            } else {
                BytesArray bytesArray = text.bytes().toBytesArray();
                generator.writeUTF8String(bytesArray.array(), bytesArray.arrayOffset(), bytesArray.length());
            }
        } else if (value instanceof ToXContent) {
            ((ToXContent) value).toXContent(this, ToXContent.EMPTY_PARAMS);
        } else if (value instanceof double[]) {
            generator.writeStartArray();
            for (double v : (double[]) value) {
                generator.writeNumber(v);
            }
            generator.writeEndArray();
        } else if (value instanceof long[]) {
            generator.writeStartArray();
            for (long v : (long[]) value) {
                generator.writeNumber(v);
            }
            generator.writeEndArray();
        } else if (value instanceof int[]) {
            generator.writeStartArray();
            for (int v : (int[]) value) {
                generator.writeNumber(v);
            }
            generator.writeEndArray();
        } else if (value instanceof float[]) {
            generator.writeStartArray();
            for (float v : (float[]) value) {
                generator.writeNumber(v);
            }
            generator.writeEndArray();
        } else if (value instanceof short[]) {
            generator.writeStartArray();
            for (short v : (short[]) value) {
                generator.writeNumber(v);
            }
            generator.writeEndArray();
        } else {
            // if this is a "value" object, like enum, DistanceUnit, ..., just toString it
            // yea, it can be misleading when toString a Java class, but really, jackson should be used in that case
            generator.writeString(value.toString());
            //throw new ElasticsearchIllegalArgumentException("type not supported for generic value conversion: " + type);
        }
    }

  其中类中定义了成员变量DateTimeFormatter defaultDatePrinter = ISODateTimeFormat.dateTime().withZone(DateTimeZone.UTC);   
我们看下这部分:XContentBuilder.defaultDatePrinter.print(((Date) value).getTime()) 进去后
 

/**
     * Prints a millisecond instant to a String.
     * <p>
     * This method will use the override zone and the override chronology if
     * they are set. Otherwise it will use the ISO chronology and default zone.
     *
     * @param instant  millis since 1970-01-01T00:00:00Z
     * @return the printed result
     */
    public String print(long instant) {
        StringBuilder buf = new StringBuilder(requirePrinter().estimatePrintedLength());
        try {
            printTo((Appendable) buf, instant);
        } catch (IOException ex) {
            // StringBuilder does not throw IOException
        }
        return buf.toString();
    }

 看到这里,想必各位应该都知道es最终是如何存储Date类型的了。是的,它最终的输出方式都是以字符串输出,只是默认的格式是:1970-01-01T00:00:00Z ,也就是默认的 UTC 格式。
       如果不了解其机制,我们将Date类型的时间字段format成我们自定义的格式,然后插入es,那么就会报解析异常的错误。或者我们将type为date,格式为自定义的,如yyyy-MM-dd的es字段反序列化为对应Date类型,也会报相应的错误,如下:

Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of java.util.Date from String value '2017-06-30 15:41:24': not a valid representation (error: Failed to parse Date value '2017-06-30 15:41:24': Can not parse date "2017-06-30 15:41:24": not compatible with any of standard forms ("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "EEE, dd MMM yyyy HH:mm:ss zzz", "yyyy-MM-dd"))
 at [Source: java.io.StringReader@3d408487; line: 13, column: 24] (through reference chain: com.qunar.finance.nbdata.common.bean.vehicle.cargraph.CarUserResult["createTime"])

     当然,如果我们了解了上面的问题,直接使用Date类型存储时间是没问题的。但是需要注意的是,时区要少了8小时,我们要主动加上。通常在我们使用时间的时候,一般不会考虑时区的因素,而且存储的格式也不会保存相应的时区,不符合我们的日常使用习惯。那如何更好的存储时间字段呢

    1 直接使用long类型的时间戳,缺点是不直观

    2 设置为String类型,对相应的时间做自定义的format,mapping中要记得字段设置为date类型,format格式和你自定义的保持一致

   在实际应用中我们通常也是选择上面两者之一。

   我在项目中的做法,将日期格式转换

    public class CommonUtil {

           private static SimpleDateFormat gmtSdf = new SimpleDateFormat("yyyy-MM-    
               dd'T'HH:mm:ss.SSSZ");

           public static synchronized String format2GmtTime(Date date) {
               return gmtSdf.format(date);
           }
    }


 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值