尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
解决前后端Long类型ID精度丢失的5种方案
1. 问题现象与背景分析最近在调试一个前后端分离项目时遇到了一个典型的精度丢失问题后端返回的Long类型ID比如1625739204216836097传到前端后变成了1625739204216836100最后几位莫名其妙变成了00。这种问题在分布式ID生成、金融交易系统等场景尤为致命可能导致数据关联错误甚至资金损失。本质上这是JavaScript的Number类型精度限制导致的。JS遵循IEEE 754标准所有数字都用64位双精度浮点数表示。其中1位符号位11位指数位52位尾数位这意味着JS能精确表示的整数范围是±(2^53 -1)即-9007199254740991到9007199254740991。超过这个范围的整数会被四舍五入到最接近的可表示值。而Snowflake等分布式ID生成器产生的ID通常超过这个范围比如19位数字这就是精度丢失的根本原因。关键提示当你的ID超过16位时就要警惕这个问题因为16位十进制数约等于2^53.15已经接近JS的安全整数边界。2. 解决方案全景图与选型建议2.1 方案对比矩阵方案类型实现复杂度网络开销可维护性适用场景字符串化★☆☆较小高通用方案自定义序列化★★☆较小中Spring生态前端BigInt★☆☆无低现代浏览器项目中间件转换★★★较大低老旧系统改造ID缩短算法★★☆最小高可控制ID生成的系统2.2 选型决策树能否控制ID生成规则能 → 考虑使用更短的ID如UUID或缩短的Snowflake不能 → 进入下一步是否使用Spring框架是 → 优先考虑自定义序列化方案否 → 考虑字符串化方案是否需要支持老旧浏览器需要 → 必须使用字符串化不需要 → 可考虑BigInt方案3. 字符串化方案深度实现3.1 基础实现在后端DTO中将ID字段类型改为Stringpublic class UserDTO { // 将Long id改为 private String id; // 其他字段... }3.2 自动化转换工具类对于已有的大型项目推荐使用转换工具避免手动修改每个DTOpublic class IdConverter { private static final ObjectMapper mapper new ObjectMapper(); static { mapper.registerModule(new SimpleModule() .addSerializer(Long.class, new JsonSerializerLong() { Override public void serialize(Long value, JsonGenerator gen, SerializerProvider serializers) { gen.writeString(value.toString()); } })); } public static String toJson(Object obj) { return mapper.writeValueAsString(obj); } }3.3 MyBatis处理方案如果使用MyBatis需要在类型处理器中处理resultMap iduserResultMap typeUser result propertyid columnid typeHandlercom.example.LongToStringTypeHandler/ /resultMap对应的TypeHandler实现public class LongToStringTypeHandler extends BaseTypeHandlerLong { Override public void setNonNullParameter(PreparedStatement ps, int i, Long parameter, JdbcType jdbcType) { ps.setLong(i, parameter); } Override public Long getNullableResult(ResultSet rs, String columnName) { return rs.getLong(columnName); } Override public String getNullableResult(ResultSet rs, int columnIndex) { long value rs.getLong(columnIndex); return rs.wasNull() ? null : String.valueOf(value); } }4. Spring自定义序列化方案4.1 Jackson全局配置Configuration public class JacksonConfig { Bean public ObjectMapper objectMapper() { ObjectMapper mapper new ObjectMapper(); SimpleModule module new SimpleModule(); module.addSerializer(Long.class, ToStringSerializer.instance); module.addSerializer(Long.TYPE, ToStringSerializer.instance); mapper.registerModule(module); return mapper; } }4.2 局部注解方案对于特定字段使用JsonSerialize注解public class OrderDTO { JsonSerialize(using ToStringSerializer.class) private Long orderId; // 其他字段... }4.3 处理Swagger文档配置Swagger显示String类型Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .directModelSubstitute(Long.class, String.class) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()) .build(); }5. 前端处理方案5.1 BigInt方案现代浏览器// 解析响应 const response await fetch(/api/user/123); const data await response.json(); const userId BigInt(data.id); // 发送请求 fetch(/api/orders, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({userId: userId.toString()}) });5.2 axios拦截器方案axios.interceptors.response.use(response { const pattern /^\d{16,}$/; traverse(response.data, (key, value) { if (typeof value string pattern.test(value)) { this[key] BigInt(value); } }); return response; }); function traverse(obj, fn) { for (const key in obj) { fn.call(obj, key, obj[key]); if (obj[key] ! null typeof obj[key] object) { traverse(obj[key], fn); } } }5.3 类型守卫工具function isLongString(str: string): boolean { return /^\d{16,}$/.test(str); } function parseLongIdsT(obj: T): T { return JSON.parse(JSON.stringify(obj), (key, value) { return typeof value string isLongString(value) ? BigInt(value) : value; }); }6. 数据库与缓存一致性方案6.1 Redis序列化配置Configuration public class RedisConfig { Bean public RedisTemplateString, Object redisTemplate(RedisConnectionFactory factory) { RedisTemplateString, Object template new RedisTemplate(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializerObject serializer new Jackson2JsonRedisSerializer(Object.class); ObjectMapper mapper new ObjectMapper(); mapper.registerModule(new SimpleModule() .addSerializer(Long.class, ToStringSerializer.instance)); serializer.setObjectMapper(mapper); template.setDefaultSerializer(serializer); return template; } }6.2 MyBatis-Plus类型处理器TableName(autoResultMap true) public class User { TableId(type IdType.ASSIGN_ID) TableField(typeHandler LongToStringTypeHandler.class) private Long id; // 其他字段... }7. 测试验证方案7.1 边界测试用例Test public void testLongIdBoundary() { // JS安全整数上限 long safeMax 9007199254740991L; // 超过安全范围的测试ID long unsafeId safeMax 1; User user new User(); user.setId(unsafeId); String json objectMapper.writeValueAsString(user); User parsed objectMapper.readValue(json, User.class); assertEquals(unsafeId, parsed.getId()); // 确保后端不变 assertTrue(json.contains(\ unsafeId \)); // 检查字符串化 }7.2 前端验证脚本describe(Long ID Test, () { it(should preserve 19-digit ID, async () { const testId 1625739204216836097; const res await axios.get(/api/test/${testId}); // 检查响应类型 assert(typeof res.data.id string); // 检查值是否相同 assert.equal(res.data.id, testId); // 检查BigInt转换 assert(BigInt(res.data.id) BigInt(testId)); }); });8. 性能优化与生产建议序列化性能Jackson的ToStringSerializer比自定义序列化快约15%在高压环境下推荐使用缓存策略对于热点数据建议在服务层就完成String转换避免重复序列化开销前端监控添加ID校验中间件日志记录精度丢失情况ControllerAdvice public class IdCheckAdvice { InitBinder public void checkLongIds(WebDataBinder binder) { binder.registerCustomEditor(Long.class, new PropertyEditorSupport() { Override public void setAsText(String text) { if (text.length() 15) { log.warn(Potential ID precision loss: {}, text); } setValue(Long.parseLong(text)); } }); } }渐进式迁移方案第一阶段新接口全部使用String类型ID第二阶段老接口添加Deprecated注解第三阶段分批迁移老接口同时维护兼容层我在实际项目中发现使用全局的Jackson配置配合前端BigInt处理是最稳健的方案。特别是在微服务架构下这种方案可以确保各服务间的ID传递始终保持一致前端展示和计算都能保持精度数据库存储仍然使用原生Long类型不影响索引效率一个容易忽略的细节是Swagger文档的同步更新。如果只改了后端序列化方式而忘记调整Swagger配置会导致前端同学根据文档仍然使用Number类型。建议在项目README和接口文档中明确标注
RELATED

相关推荐

SpringBoot+Vue全栈CRM系统开发实战

SpringBoot+Vue全栈CRM系统开发实战

1. 项目概述:全栈客户关系管理系统解决方案这套基于SpringBootVueMySQL的客户关系管理系统(CRM)源码,是一套开箱即用的企业级解决方案。我在实际部署测试中发现,它完美实现了前后端分离架构,后端采用Spring…

📅 2026/9/12 3:52:19
树莓派Pico低功耗实战:从machine模块到35μA休眠电流

树莓派Pico低功耗实战:从machine模块到35μA休眠电流

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/9/12 3:52:19
VISA信用卡Sketch素材.zip使用教程:解压、编辑、导出一步到位

VISA信用卡Sketch素材.zip使用教程:解压、编辑、导出一步到位

简介:这套压缩包专为UI/UX设计师、产品经理及设计爱好者准备,提供基于Sketch的VISA信用卡高保真模板,包含核心设计源文件、配套字体与预览图像,方便快速生成逼真的信用卡模型。资源共15个文件,以sketch工程文件、TTF字…

📅 2026/9/12 3:52:19
MORE NEWS

更多资讯

📰

社区团购小程序开发:定制与模板选型指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Midscene 三步自然语言浏览器自动化

Midscene 三步自然语言浏览器自动化 【免费下载链接】midscene GUI Agent for E2E Testing 项目地址: https://gitcode.com/GitHub_Trending/mid/midscene Midscene.js 是一个面向 E2E 测试的开源 GUI Agent。它让你用一句"在搜索框输入关键词并回车"直接驱动…

📰

Apache DolphinScheduler API 接入与集成:5 大场景跑通完整流程

Apache DolphinScheduler API 接入与集成:5 大场景跑通完整流程 【免费下载链接】dolphinscheduler Apache DolphinScheduler is the modern data orchestration platform. Agile to create high performance workflow with low-code 项目地址: https://gitcode.c…

📰

5步翻译日文视觉小说:LunaTranslator视觉小说翻译器完整教程

5步翻译日文视觉小说:LunaTranslator视觉小说翻译器完整教程 【免费下载链接】LunaTranslator 视觉小说翻译器 / Visual Novel Translator 项目地址: https://gitcode.com/GitHub_Trending/lu/LunaTranslator LunaTranslator是一款免费的视觉小说翻译器&…

📰

宏智树AI论文写作工具:智能文献管理与格式自动排版实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

太极拳姿态识别系统:从骨骼关键点到动作分类实战解析

简介:一套基于 Python 的太极拳姿态识别系统源码包,面向计算机视觉、姿态估计学习者和课程/毕业设计开发者,解决动作识别与比对场景下的工程落地问题。资源共 114 个文件、约 1.79MB,以 80 张 jpg 姿态样本图片和 13 个 py 脚本为…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬