一、问题描述
前后端在进行数据交互的时候,通过雪花算法生成的 id 主键出现前后端不一致的情况;
二、问题分析
使用雪花算法生成的 id 主键超出了 前端 js 的 number 类型的最大数值;
- JAVA 中的 Long 类型的最大值为(2^63-1)也就是 9223372036854775807;
- JS 的 number 类型所支持的最大值为 (2^53)也就是 9007199254740992;
- 一旦前端所接收的数字超过了 JS 的 number 类型的最大值,就会出现精度丢失,从而导致前后端的值不一致。
三、解决方案
解决思路:后端的 ID(Long) 通过转换成 String 类型提供给前端使用,前端使用 js 中的 string 就不会出现精度丢失了。 而前端把 String 类型的数字传回服务端的时候,可以直接使用 Long 类型进行接收( Spring 反序列化参数接收默认支持的行为)。
3.1 方案一:添加 yaml 配置
通过在 application.yml 中加上以下配置进行将所有数字都变成字符串,包括 long 和 int 类型;
spring:jackson:generator:writeNumbersAsStrings: true
3.2 方案二:引入注解
@JsonFormat 或者 @JsonSerialize
// @JsonFormat 注解示例
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;@Data
public class User {/*** 用户id*/@JsonFormat(shape = JsonFormat.Shape.STRING)private Long id;
}
// @JsonSerialize 注解示例
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;@Data
public class User {/*** 用户id*/@JsonSerialize(using = ToStringSerializer.class)private Long id;
}
四、为什么不直接用String 作为主键
就该问题上来说,使用 String 确实能解决精度丢失的问题,但是使用字符串不建议直接作为主键,理由如下:
-
存储空间:相比于数值类型,字符串通常占用更多的存储空间。如果数据集很大或者主键被频繁使用,字符串类型的主键可能会占用大量的存储空间,增加存储成本。
-
查询性能:字符串类型的主键在进行索引和查询时通常比数值类型的主键更慢。字符串比较需要逐个字符进行比较,而数值类型可以进行简单的比较操作。当数据集较大时,字符串比较的开销可能会显著影响查询性能。
-
排序和范围查询:字符串类型的主键在进行排序和范围查询时可能会遇到一些困难。字符串排序通常是基于字符的字典顺序进行的,而不是基于数值大小。这可能导致排序结果不符合预期。另外,对于范围查询,字符串类型的主键可能需要进行额外的计算和处理才能正确执行范围查询。