Use of LocalDateTime in the project (LocalDateTime docking front end interacts with timestamp, LocalDateTime docking database)

Keywords: Java JSON Database SpringBoot Spring

Catalog

1. Blog writing background

Background for this article: Since Date and Timestamp objects are no longer recommended in JDK 8, I plan to use LocalDateTime in a new project for my company.

Because of my project group, the uniform interaction of front and back time is timestamp, which can not be directly converted by fastJson or jackson, I have stepped on a lot of pits, and I recommend that you be patient to read through.

The results are as follows:

  1. Front End Delivery Timestamp
{
	"localDateTime": 1584700466000
}
  1. Backend Return Timestamp
{
    "code": "0",
    "desc": "Request succeeded",
    "data": {
        "localDateTime": 1584700466000
    }
}

========================================================

If you feel more nonsense, just look directly at the label
I don't want to write a direct conclusion when I write this blog. I want to share the process of treading a pit with my readers.

========================================================

2. LocalDateTime front-end interaction

2.1 LocalDateTime writes a timestamp to the front end

2.1.1 fastJson default write format

This project uses fastJson to write the front end. Let's look at the following code

  1. Write back front VO object
@Data
public class LocalDateTimeVO {
    private LocalDateTime localDateTime;
}
  1. test method
public static void main(String[] args) {
    LocalDateTimeVO localDateTimeVO = new LocalDateTimeVO();
    localDateTimeVO.setLocalDateTime(LocalDateTime.now());
    String json = JSON.toJSONString(localDateTimeVO);
    System.out.println(json);
}
  1. console output
{"localDateTime":"2020-03-12T23:00:28.747"}

As you can see from the diagram above, the service side does not normally return the timestamp to the front end.It does not meet the requirements.

2.1.2 Change the fastJson write format so that it writes back the timestamp ()

  1. fastJson provides a way to customize the json transformation @JSONFiled, so we add serializeUsing and assign it to our custom serialization controller.
  2. Customize fastJson serialization converter, override ObjectSerializer
/**
 * Since the LocalDateTime type cannot be converted to a string when converting JSON, only the specified pattern type can be converted with @JsonFormat, so we need to customize a serialization executor
 * LocalDateTime Serialization (converting the LocalDateTime type to a timestamp and returning it to the front end)
 *
 * @author Chimm Huang
 * @date 2020/3/7
 */
public class LocalDateTimeSerializer implements ObjectSerializer {
    @Override
    public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
        if (object != null) {
            LocalDateTime localDateTime = (LocalDateTime) object;
            //Converts the localDateTime to a Chinese region (+8) timestamp.
            serializer.write(localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
        } else {
            serializer.write(null);
        }
    }
}
  1. Use our own fastJson serialization converter
@Data
public class LocalDateTimeVO {
    @JSONField(serializeUsing = LocalDateTimeSerializer.class)
    private LocalDateTime localDateTime;
}
  1. Execute test method again, console output
{"localDateTime":1584026032912}

You can see that the LocalDateTime has been successfully converted to a timestamp and can be returned to the front end.

2.2 Receive the time stamp passed by the front end as LocalDateTimme

2.2.1 LocalDateTime Default Received Format

Whether we pass a timestamp (1584026032912) or a custom format ("2020-03-13"), an error of 400 will be reported when accepted by the server.That is, the incoming format is incorrect and cannot be converted by spring to LocalDateTime

After my rough test, I found that the default acceptance format is a format unique to LocalDateTime, namely, 2020-03-12T23:00:28.747, which will all be reported 400.The only difference between this format and the Date format is that Date is distinguished by space between day and time, while LocalDateTime is distinguished by T.

2.2.2 Change the fastJson deserialization method so that it can convert the timestamp to LocalDateTime

  1. The @JSONField comment provided by fastJson includes the designation of a deserialized converter, so we override its method, ObjectDeserializer
/**
 * Since timestamps cannot be converted directly by fastJSON to the LocalDateTime type, we need to customize a serialization executor
 * LocalDateTime Deserialization (converting the timestamp passed by the front end to the LocalDateTime type)
 *
 * @author Chimm Huang
 * @date 2020/3/7
 */
public class LocalDateTimeDeserializer implements ObjectDeserializer {

    @Override
    @SuppressWarnings("unchecked")
    public LocalDateTime deserialze(DefaultJSONParser parser, Type type, Object fieldName) {

        String timestampStr = parser.getLexer().numberString();

        if (timestampStr == null || "".equals(timestampStr)) {
            return null;
        }

        timestampStr = timestampStr.replaceAll("\"", "");

        long timestamp = Long.parseLong(timestampStr);
        if(timestamp == 0) {
            return null;
        }
        return Instant.ofEpochMilli(timestamp).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
    }

    @Override
    public int getFastMatchToken() {
        return 0;
    }
}
  1. Use our own fastJson deserialization converter
@Data
pubcli class LocalDateTimeVO {
    @JSONField(serializeUsing = LocalDateTimeSerializer.class, deserializeUsing = LocalDateTimeDeserializer.class)
    private LocalDateTime localDateTime;
}
  1. test method
public static void main(String[] args) {
    String json = "{\"localDateTime\":1584026032912}";
    LocalDateTimeVO localDateTimeVO = JSON.parseObject(json, LocalDateTimeVO.class);
    System.out.println(localDateTimeVO);
}
  1. Console Execution Results Display
LocalDateTimeVO(localDateTime=2020-03-12T23:13:52.912)

You can see that the timestamp was successfully accepted by fastJson and converted to LocalDateTime.

2.2.3 [pit] Change SpringBoot's @RequestBody to receive fastJson

When you see this heading, you'll be confused. Our project isn't using fastJson right now
Are you?
The fact that I tested it was that we used fastJson for the conversion when we wrote back the front end, but when we accepted the Json, we used Spring's default jackson, so this would cause us to override the deserialization method of fastJson and not execute it.The front end passes a timestamp to the back end, and the back end misses 400.

Therefore, we need to change the default jackson provided by spring to fastJson

/**
 * springboot By default, jackson wraps requestBody requests, and the project switches to fastJson for request wrapping and response
 * Configure springboot to use fastJson for request acceptance and response to data
 *
 * @author Chimm Huang
 * @date 2020/3/7
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {

    public HttpMessageConverter<String> stringConverter() {
        return new StringHttpMessageConverter(StandardCharsets.UTF_8);
    }

    public FastJsonHttpMessageConverter fastConverter() {
        //1. Define an object for convert ing the message
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        //2. Add configuration information for fastJson
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(
                SerializerFeature.WriteMapNullValue,
                SerializerFeature.WriteNullStringAsEmpty,
                SerializerFeature.WriteNullNumberAsZero,
                SerializerFeature.WriteNullListAsEmpty,
                SerializerFeature.WriteNullBooleanAsFalse);

        fastJsonConfig.setCharset(StandardCharsets.UTF_8);
        //2-1 Handling Chinese Scrambling
        List<MediaType> fastMediaTypes = new ArrayList<>();
        fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
        fastConverter.setSupportedMediaTypes(fastMediaTypes);
        //3. Add configuration information to convert
        fastConverter.setFastJsonConfig(fastJsonConfig);
        return fastConverter;
    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.clear();
        converters.add(stringConverter());
        converters.add(fastConverter());
    }
}

When the configuration is complete, the interaction between the back end and the front end using time stamps is complete.

3. LocalDateTime interacts with the database

It's easier to interact with the database, and we're using version 3.4.5 of mybatis.And the database time type is: datetime
We just need to introduce jsr310 coordinates in the pom file

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-typehandlers-jsr310</artifactId>
    <version>1.0.2</version>
</dependency>

3.1 [pit] Database interaction LocalDateTime is rounded

LocalDateTime can be precise to nanoseconds, but the database datetime type defaults to precise to seconds if no length is specified.This results in a maximum LocalDateTime, such as 2020-04-01T23:59:59.9999999, rounded to 2020-04-02 00:00:00 when stored in the database.

Solution 1:
Reset the maximum time of the LocalDateTime to set the maximum precision to seconds.

Solution 2:
Set the length of the database's datetime type to 6 (datetime(6), or microseconds), and reset the maximum precision of the LocalDateTime to the corresponding subtlety.

Both schemes call the withNano() method of LocalDateTime

public static void main(String[] args) {
    LocalDateTime now = LocalDateTime.now();
    LocalDateTime todayMax = LocalDateTime.of(now.toLocalDate(), LocalTime.MAX);
    // Output the time of the day
    System.out.println(now);
    // Maximum time of output day (default maximum)
    System.out.println(todayMax);
    // Maximum time of the output day (when datetime precision is seconds)
    System.out.println(todayMax.withNano(0));
    // The maximum time of the output day (when datetime precision is milliseconds) datetime(3)
    System.out.println(todayMax.withNano(999000000));
    // The maximum time of the output day (when datetime precision is microseconds) datetime(6)
    System.out.println(todayMax.withNano(999999000));
}

console output

2020-04-01T09:50:46.830845400
2020-04-01T23:59:59.999999999
2020-04-01T23:59:59
2020-04-01T23:59:59.999
2020-04-01T23:59:59.999999

Posted by haddydaddy on Mon, 13 Apr 2020 18:47:43 -0700