您好,欢迎访问一九零五行业门户网

SpringBoot雪花算法主键ID传到前端后精度丢失问题如何解决

问题描述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; @datapublic 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; @datapublic class uservo { private long id; private string username;}
测试
访问:http://localhost:8080/user/find?id=1352213368413982722
结果
问题复现从上边可以看到,并没有问题。
为什么没有出问题?
前端传入后端:spingmvc会自动将string类型的id转为long类型,不会出问题后端响应给前端:是json格式,与js没有关系,不会出问题
什么时候会出问题?
前端接收到json之后,将其序列化为js对象,然后进行其他操作。在json转js对象时就会出问题,如下:
可以看到,原来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; @configurationpublic 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
方案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 最大最小值 处理 */@jacksonstdimplpublic 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; @configurationpublic 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
法2:局部处理说明
在字段上加:@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; @datapublic class uservo { @jsonserialize(using= tostringserializer.class) private long id; private string username;}
测试 
访问:http://localhost:8080/user/find?id=1352213368413982722
以上就是springboot雪花算法主键id传到前端后精度丢失问题如何解决的详细内容。
其它类似信息

推荐信息