LocalDateTime在項目中的使用(LocalDateTime對接前端通過時間戳互轉、LocalDateTime對接數據庫)


1. 博客編寫背景

本文章的編寫背景:由於在 JDK 8 中,Date、Timestamp 對象已經不推薦使用,所以在公司的新項目上,我計划將 LocalDateTime 使用在新項目中。

由於本人所在項目組,統一的前后端時間的交互方式為時間戳,而時間戳並不能直接被fastJsonjackson 直接轉換,所以踩了不少的坑,個人建議有耐心的看完。

實現的效果如下:

  1. 前端傳遞時間戳
{
	"localDateTime": 1584700466000
}
  1. 后端返回時間戳
{
    "code": "0",
    "desc": "請求成功",
    "data": {
        "localDateTime": 1584700466000
    }
}

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

若是感覺廢話比較多,那么直接看標注了【★★★】的即可
個人寫這個博客,並不想直接寫結論,更多的是想給讀者分享踩坑的過程

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


2. LocalDateTime 前端交互

2.1 LocalDateTime 向前端寫入時間戳

2.1.1 fastJson 默認的寫入格式

本項目使用的是 fastJson 會寫前端,我們先看下以下代碼

  1. 回寫前端的 VO 對象
@Data
public class LocalDateTimeVO {
    private LocalDateTime localDateTime;
}
  1. 測試方法
public static void main(String[] args) {
    LocalDateTimeVO localDateTimeVO = new LocalDateTimeVO();
    localDateTimeVO.setLocalDateTime(LocalDateTime.now());
    String json = JSON.toJSONString(localDateTimeVO);
    System.out.println(json);
}
  1. 控制台輸出
{"localDateTime":"2020-03-12T23:00:28.747"}

從上圖中可以看出,服務端並不能正常的返回時間戳給前端。並不符合需求。

2.1.2 更改 fastJson 寫入格式,讓其回寫時間戳 (★★★)

  1. fastJson提供了自定義 json 轉換的方法 @JSONFiled,我們添加 serializeUsing,將其指定到我們自定義的序列化控制器即可。
  2. 自定義 fastJson 序列化轉換器,重寫 ObjectSerializer
/**
 * 由於 LocalDateTime 類型在轉換 JSON 的時候,並不能被轉換為字符串,使用 @JsonFormat 只能轉換為指定的 pattern 類型,因此我們需要自定義一個序列化執行器
 * LocalDateTime 序列化(將 LocalDateTime類型 轉換為 時間戳 返回給前端 )
 *
 * @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;
            //將localDateTime轉換為中國區(+8)時間戳。
            serializer.write(localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
        } else {
            serializer.write(null);
        }
    }
}
  1. 使用我們自己寫的 fastJson 序列化轉換器
@Data
public class LocalDateTimeVO {
    @JSONField(serializeUsing = LocalDateTimeSerializer.class)
    private LocalDateTime localDateTime;
}
  1. 再次執行測試方法,控制台輸出
{"localDateTime":1584026032912}

可以看出,LocalDateTime 已經成功被轉換為了時間戳,並且可以返回給前端。

2.2 接收前端傳遞的時間戳為 LocalDateTimme

2.2.1 Post 請求參數封裝

1. LocalDateTime 默認接收的格式

不管我們傳遞時間戳(1584026032912),還是傳遞自定義格式("2020-03-13"),在服務端接受的時候,都會報錯400。也就是說,傳入的格式是錯誤的,無法被 spring 轉換為 LocalDateTime

經過我的粗略測試,我發現,默認的接受格式為 LocalDateTime 特有的格式,即:2020-03-12T23:00:28.747,除此之外都會報400。這種格式與 Date 格式的唯一區別就在於,Date之間是用空格區分的,而 LocalDateTime 是用 T 來區分的。

2. 更改 fastJson 反序列化方法,讓其能夠轉換時間戳為 LocalDateTime(★★★)

  1. fastJson 提供的 @JSONField 注解包括了反序列化轉換器的指定,因此,我們重寫其方法 ObjectDeserializer
/**
 * 由於 時間戳 並不能直接被 fastJSON 轉換為 LocalDateTime 類型,因此我們需要自定義一個序列化執行器
 * LocalDateTime 反序列化(將前端傳遞的 時間戳 轉換為 LocalDateTime 類型)
 *
 * @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. 使用我們自己寫的 fastJson 反序列化轉換器
@Data
pubcli class LocalDateTimeVO {
    @JSONField(serializeUsing = LocalDateTimeSerializer.class, deserializeUsing = LocalDateTimeDeserializer.class)
    private LocalDateTime localDateTime;
}
  1. 測試方法
public static void main(String[] args) {
    String json = "{\"localDateTime\":1584026032912}";
    LocalDateTimeVO localDateTimeVO = JSON.parseObject(json, LocalDateTimeVO.class);
    System.out.println(localDateTimeVO);
}
  1. 控制台執行結果展示
LocalDateTimeVO(localDateTime=2020-03-12T23:13:52.912)

可以看出,時間戳成功被 fastJson 接受,並轉換為了 LocalDateTime

3. 【坑】更改 SpringBoot 的 @RequestBody 為 fastJson 接收(★★★)

當你看到這個小標題時,肯定會很疑惑,我們項目目前不就是使用的 fastJson
嗎?
實際情況經過我測試,得出的結論是,我們在回寫前端的時候,是使用 fastJson 進行轉換的,但是在接受 Json 的時候,是使用 Spring 默認的 jackson 來接受的,所以這會導致,我們重寫了 fastJson 的反序列化方法並未執行。前端傳遞時間戳給后端,后端報錯400。


因此,我們需要更改 spring 默認提供的 jacksonfastJson

/**
 * springboot 默認使用的是 jackson 進行 requestBody 請求的封裝,該項目切換為使用 fastJson 進行請求封裝和響應
 * 配置 springboot 使用 fastJson 進行數據的請求接受和響應
 *
 * @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、定義一個convert轉換消息的對象
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        //2、添加fastJson的配置信息
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(
                SerializerFeature.WriteMapNullValue,
                SerializerFeature.WriteNullStringAsEmpty,
                SerializerFeature.WriteNullNumberAsZero,
                SerializerFeature.WriteNullListAsEmpty,
                SerializerFeature.WriteNullBooleanAsFalse);

        fastJsonConfig.setCharset(StandardCharsets.UTF_8);
        //2-1 處理中文亂碼問題
        List<MediaType> fastMediaTypes = new ArrayList<>();
        fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
        fastConverter.setSupportedMediaTypes(fastMediaTypes);
        //3、在convert中添加配置信息
        fastConverter.setFastJsonConfig(fastJsonConfig);
        return fastConverter;
    }

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

配置完成之后,后端與前端使用時間戳進行交互已完成。

2.2.2 GET 請求參數封裝

只需自定義一個轉換類即可

/**
 * LocalDateTime 作為 作為 RequestParam 或者 PathVariable 時,將前端傳遞的時間戳轉換
 *
 * @author Chimm Huang
 * @date 2020/04/16
 */
@Configuration
public class LocalDateTimeGetConverter {
    @Bean
    public Converter<String, LocalDateTime> localDateTimeConverter() {
        return new Converter<String, LocalDateTime>() {
            @Override
            public LocalDateTime convert(String source) {
                return LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(source)), ZoneId.systemDefault());
            }
        };
    }
}

實例代碼:

@GetMapping("/soutTime")
public void soutTimeByGet(LocalDateTime localDateTime) {
    System.out.println(localDateTime);
}

前端請求:

localhost:8001/api/demo/soutTime?localDateTime=1587026916000

控制台輸出:

2020-04-16T16:48:36

3. LocalDateTime 與數據庫交互(★★★)

與數據庫交互比較簡單,我們使用的 mybatis 的版本為 3.4.5。且數據庫時間類型為:datetime
我們只需要在 pom 文件中引入 jsr310 坐標即可

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

3.1 【坑】數據庫交互LocalDateTime被四舍五入(★★★)

LocalDateTime 是可以精確到納秒的,但是數據庫datetime類型如果不指定長度的話,默認是精確到秒的。這就會造成,在LocalDateTime為最大值的時候,如:2020-04-01T23:59:59.999999999,存入數據庫的時候被四舍五入為了2020-04-02 00:00:00

解決方案一:
重置一下LocalDateTime的最大時間,將最大精度設置為秒。

解決方案二:
將數據庫的datetime類型長度設置為6(datetime(6)即微秒),然后將LocalDateTime的最大精度重置為對應的微妙即可。

以上兩種方案調用LocalDateTimewithNano()方法即可

public static void main(String[] args) {
    LocalDateTime now = LocalDateTime.now();
    LocalDateTime todayMax = LocalDateTime.of(now.toLocalDate(), LocalTime.MAX);
    // 輸出當天的時間
    System.out.println(now);
    // 輸出當天的最大時間(默認最大)
    System.out.println(todayMax);
    // 輸出當天的最大時間(datetime精度為秒的時候)
    System.out.println(todayMax.withNano(0));
    // 輸出當天的最大時間(datetime精度為毫秒的時候) datetime(3)
    System.out.println(todayMax.withNano(999000000));
    // 輸出當天的最大時間(datetime精度為微秒的時候) datetime(6)
    System.out.println(todayMax.withNano(999999000));
}

控制台輸出

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM