大家好,我是 EthanYuan。今天跟大家分享开发云相册平台时遇到的经典高频坑 —— 长整型 ID 精度丢失问题。这绝对是前后端联调里最容易被忽略,却又影响极大的隐患。
很多前端同学第一次踩到长整形 ID 精度丢失的坑,往往是在普通的列表页面:接口返回数据正常展示,但是点击详情、执行查询操作时,会直接提示数据不存在,完全查不到对应内容。排查了很久业务逻辑、接口参数,都找不到问题规律,最后才明白,问题根本不在业务代码,根源出在 JavaScript 本身的数字存储机制上。
原理详解:JS 装不下雪花 ID
JavaScript 的 Number 类型本质是双精度浮点数,能够精确表示的整数范围仅有 ±(253−1)。一旦后端返回的 Long 型雪花 ID 超出这个安全阈值,前端在自动解析 JSON 数据时,ID 末尾几位就会被系统静默篡改、失真。
更麻烦的是这种失真不可逆,控制台展示的数字看起来差别不大,但原始 ID 已经被改变。后续路由传参、接口查询、数据缓存、表格标识只要把 ID 当作数字使用,就会拿着错误的 ID 去请求后端接口,最终直接导致数据查询失败、找不到对应资源。
结合我云相册项目的实际场景来讲:项目后端使用 MyBatis-Plus 框架自带的分布式 ID 策略,用户表、图片表、相册表等所有核心业务实体,主键统一配置 IdType.ASSIGN_ID,底层实现就是标准雪花算法。雪花算法生成 64 位长整型 ID,由时间戳、机器 ID、数据中心 ID、序列号组合而成,分布式环境下全局唯一且趋势递增,非常适配高并发业务。数据库主键字段统一使用 bigint 类型存储,生成的 ID 基本都是 19 位超长数字, 如1985558297927299073、2028345478970564609。
这类数值早已远超 JS 安全整数上限,经过 JSON 传输解析后精度直接丢失,ID 失真之后发起查询请求,后端自然无法匹配到数据库记录,直接查不到数据。
***最优解决方案:后端 Jackson 序列化统一转字符串***
针对这个业界经典问题,我在项目中采用了后端全局统一处理的最优方案,一次配置全局生效。
在后端JsonConfig配置类中,自定义并注册ObjectMapper序列化配置,新增序列化规则:所有 Long 包装类型、long 基本数据类型,在接口序列化为 JSON 返回时,自动转换为字符串格式。
@JsonComponent
public class JsonConfig {
/**
* 添加 Long 转 json 精度丢失的配置
*/
@Bean
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
SimpleModule module = new SimpleModule();
module.addSerializer(Long.class, ToStringSerializer.instance);
module.addSerializer(Long.TYPE, ToStringSerializer.instance);
objectMapper.registerModule(module);
return objectMapper;
}
}
原本数据库里的长数字 ID 1985558297927299073,响应给前端时会完整转为字符串格式 "1985558297927299073"。JS 接收字符串不会产生任何精度损耗,ID 完整无损传递,后续接口查询完全正常。
该方案优势十分明显:
- 对前端完全无感、透明适配,前端无需修改任何业务代码;
- 数据库依旧使用
bigint数值类型,不影响主键索引查询性能; - 全局统一配置,所有接口自动生效,从根源杜绝精度丢失问题。
前端兜底通用方案
核心只有一句话:长整型 ID 全程当作字符串处理,从接入开始就不触碰 Number 类型。
在前端请求响应拦截器中,提前把所有 ID 字段强制转为字符串,后续页面渲染、路由参数、缓存键、组件唯一 key 全部使用字符串 ID。遇到极少数需要数值比较、区间运算的场景,不要转回普通 Number,统一使用BigInt进行计算;计算完成需要传输、存储时,再转回字符串即可。谨记组合原则:传输存储用字符串,数值计算用 BigInt,坚决不让普通 Number 参与 ID 流转。
总结
雪花 ID 精度丢失属于后端分布式开发的经典隐患,看似不起眼的数据类型问题,却会直接导致接口查询失效、用户数据无法访问。我在云相册项目中通过后端 Jackson 全局序列化转换 Long 为字符串,兼顾了雪花算法的分布式优势、数据库索引性能,同时完美兼容前端 JS 解析机制,是生产环境下最稳妥的最佳实践。做前后端联调时,一定要重视数据类型边界问题,很多隐蔽 BUG,根源都藏在基础类型细节里。