首頁 >Java >java教程 >SpringBoot雪花演算法主鍵ID傳到前端後精度遺失問題如何解決

SpringBoot雪花演算法主鍵ID傳到前端後精度遺失問題如何解決

PHPz
PHPz轉載
2023-05-11 12:34:182805瀏覽

問題描述

Java後端Long類型的範圍

  • -2^63~2^63,即:-9223372036854775808~ 9223372036854775807,它是19位元的。

  • 這個數字可以透過方法取得:Long.MAX_VALUE、Long_MIN_VALUE。

前端JS的數字類型的範圍

  • -2^53~2^53,即:-9007199254740991 ~9007199254740991,它是16位元的。

  • 這個數字可以透過方法取得:Number.MAX_SAFE_INTEGER、Number.MIN_SAFE_INTEGER。

結論

可見,Java後端的Long寬度大於前端的。雪花演算法一般會產生18位或19位寬度的數字,那麼這時就會出問題。

專案場景

1.表格結構

主鍵類型是BIGINT,儲存雪花演算法產生的ID。

CREATE TABLE `user` (
  `id` BIGINT(32) NOT NULL COMMENT '用户id',
	...
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

2.Entity

用Long 類型對應資料庫ID的BIGINT類型。

這裡使用 MybatisPlus 的雪花演算法自動產生19位元長度的純數字作為主鍵ID。 (當然也可以手動用雪花演算法產生ID)

import lombok.Data;
 
@Data
public class User {
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
	
    //其他成员
}

3.回應給前端

以JSON資料回應到前端正常

{
  "id": 1352166380631257089,
   ...
}

問題描述

實例

Controller

package com.knife.controller;
 
import com.knife.entity.UserVO;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
@RequestMapping("user")
public class UserController {
 
    @GetMapping("find")
    public UserVO find(Long id) {
        UserVO userVO = new UserVO();
        userVO.setId(id);
        userVO.setUsername("Tony");
 
        return userVO;
    }
}

Entity

package com.knife.entity;
 
import lombok.Data;
 
@Data
public class UserVO {
    private Long id;
 
    private String username;
}

測試

存取:http://localhost:8080/user/find ?id=1352213368413982722

結果

SpringBoot雪花演算法主鍵ID傳到前端後精度遺失問題如何解決

問題重現

從上邊可以看到,並沒有問題。

為什麼沒有出問題?

前端傳入後端:SpingMVC會自動將String類型的ID轉換為Long類型,不會出問題後端回應給前端:是JSON格式,與JS沒有關係,不會出問題

什麼時候會出問題?

前端接收到JSON之後,將其序列化為JS對象,然後進行其他操作。在JSON轉JS物件時就會出問題,如下:

SpringBoot雪花演算法主鍵ID傳到前端後精度遺失問題如何解決

可以看到,原來id為1352213368413982722,序列化為JS物件後變成了 1352213368413982700

程式碼為:

const json = '{"id": 1352213368413982722, "name": "Tony"}';
const obj = JSON.parse(json);
 
console.log(obj.id);
console.log(obj.name);

解決方案

有以下兩種方案

  • 將資料庫表設計的id欄位由Long 類型改成String 類型。

  • 前端用String類型來保存ID保持精確度,後端及資料庫繼續使用Long(BigINT)類型

方案1使用String 類型做資料庫ID,查詢效能會大幅下降。所以應該採用方案2。本文介紹方案2。

法1:全域處理

簡介

自訂ObjectMapper。

方案1:ToStringSerializer(建議)

package com.knife.config;
 
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
 
@Configuration
public class JacksonConfig {
 
    @Bean
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
 
        // 全局配置序列化返回 JSON 处理
        SimpleModule simpleModule = new SimpleModule();
        // 将使用String来序列化Long类型
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        objectMapper.registerModule(simpleModule);
        return objectMapper;
    }
}

測試 

存取:http://localhost:8080/user/find?id=1352213368413982722

SpringBoot雪花演算法主鍵ID傳到前端後精度遺失問題如何解決

方案2:自訂序列化器(不建議)

序列化器

package com.knife.config;
 
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
 
import java.io.IOException;
 
/**
 * 超出 JS 最大最小值 处理
 */
@JacksonStdImpl
public class BigNumberSerializer extends NumberSerializer {
 
	/**
	 * 根据 JS Number.MAX_SAFE_INTEGER 与 Number.MIN_SAFE_INTEGER 得来
	 */
	private static final long MAX_SAFE_INTEGER = 9007199254740991L;
	private static final long MIN_SAFE_INTEGER = -9007199254740991L;
 
	/**
	 * 提供实例
	 */
	public static final BigNumberSerializer instance = new BigNumberSerializer(Number.class);
 
	public BigNumberSerializer(Class<? extends Number> rawType) {
		super(rawType);
	}
 
	@Override
	public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException {
		// 超出范围 序列化位字符串
		if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) {
			super.serialize(value, gen, provider);
		} else {
			gen.writeString(value.toString());
		}
	}
}

ObjectMapper設定

package com.knife.config;
 
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
 
@Configuration
public class JacksonConfig {
 
    @Bean
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
 
        // 全局配置序列化返回 JSON 处理
        SimpleModule simpleModule = new SimpleModule();
        // 将使用自定义序列化器来序列化Long类型
        simpleModule.addSerializer(Long.class, BigNumberSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, BigNumberSerializer.instance);
        objectMapper.registerModule(simpleModule);
        return objectMapper;
    }
}

測試 

#存取:http://localhost:8080/user/find?id=1352213368413982722

SpringBoot雪花演算法主鍵ID傳到前端後精度遺失問題如何解決

#說明

在欄位上加上:@JsonSerialize(using= ToStringSerializer.class)。

實例

package com.knife.entity;
 
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
 
@Data
public class UserVO {
    @JsonSerialize(using= ToStringSerializer.class)
    private Long id;
 
    private String username;
}

測試 

#存取:http://localhost:8080/user/find?id=1352213368413982722

SpringBoot雪花演算法主鍵ID傳到前端後精度遺失問題如何解決

############################# ######

以上是SpringBoot雪花演算法主鍵ID傳到前端後精度遺失問題如何解決的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除