数据库bigInt,后端Long类型定义的ID,导致前端与后台不一致

摘要:项目开发中前后端数据交互常会使用id作为主键索引,通常id数值都不大,使用number类型就可以表示处理,但对于一些分布式id或其他情况,后台数据库使用雪花ID,数据库使用bigInt类型存储

项目开发中前后端数据交互常会使用id作为主键索引,通常id数值都不大,使用number类型就可以表示处理,但对于一些分布式id或其他情况,后台数据库使用雪花ID,数据库使用bigInt类型存储,比如:

33978617558956897

这个id长度17位,封装成对象传到前端后变成了:

33978617558956896

起初在后端来排除,发现后端没有问题,那么问题只能是在前端,当后端返回了这样一个id数值的数据,可以看到此数值已经超过了JS的最大处理数,丢失了精度,前端此时拿到的id值是错误的。


出现的原因:

后端: 生成的是18位的纯数字,javaLong类型可以接收。

前端:js中数字类型最长为17位,它能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值。

Math.pow(2, 53) // 9007199254740992
9007199254740992 // 9007199254740992
9007199254740993 // 9007199254740992
Math.pow(2, 53) === Math.pow(2, 53) + 1

上面代码中,超出 2 的 53 次方之后,一个数就不精确了。ES6 引入了Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量,用来表示这个范围的上下限。

Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1
// true
Number.MAX_SAFE_INTEGER === 9007199254740991
// true

Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER
// true
Number.MIN_SAFE_INTEGER === -9007199254740991
// true

上面代码中,可以看到 JavaScript 能够精确表示的极限。


解决方案:

方案一:转为字符串方式

通过后端解决,把id转化为字符串类型返回,在字段上添加一个序列化格式注解即可:

@JsonSerialize(using = ToStringSerializer.class)
private Long studentId;

这样获取到的id就是正确的id了,由后端来改是最方便快捷的。


方案二:bigint

JavaScript新增的基础数据类型bigint就可以解决此类问题,将id转化为bigint类型,使用到json-bigint插件处理json数据中的这类数值。

安装json-bigint:

npm i json-bigint

下面是使用它的一个简单示例。

const jsonStr = '{ "art_id": 1245953273786007552 }'

console.log(JSON.parse(jsonStr)) // 1245953273786007600
// JSON.stringify()

// JSONBig 可以处理数据中超出 JavaScript 安全整数范围的问题
console.log(JSONBig.parse(jsonStr)) // 把 JSON 格式的字符串转为 JavaScript 对象

// 使用的时候需要把 BigNumber 类型的数据转为字符串来使用
console.log(JSONBig.parse(jsonStr).art_id.toString()) // 1245953273786007552

console.log(JSON.stringify(JSONBig.parse(jsonStr)))

console.log(JSONBig.stringify(JSONBig.parse(jsonStr))) // 把 JavaScript 对象 转为 JSON 格式的字符串转

json-bigint 会把超出 JS 安全整数范围的数字转为一个 BigNumber 类型的对象,对象数据是它内部的一个算法处理之后的,我们要做的就是在使用的时候转为字符串来使用。

请求中使用:

通过 Axios 请求得到的数据都是 Axios 处理(JSON.parse)之后的,我们应该在 Axios 执行处理之前手动使用 json-bigint 来解析处理。Axios 提供了自定义处理原始后端返回数据的 API:transformResponse 。

import axios from 'axios'
// 遇到问题:后端返回的超大数字无法在JS中精确表示
// 解决方案:借助 json-bigint 将超大数字转成字符串即可
import bigint from 'json-bigint' const JSONBig = bigint({"storeAsString": true}) // 通过该配置,让超大数字转为字符串 const request = axios.create({
  baseURL: 'http://ttapi.research.itcast.cn/', // 接口基础路径
  // transformResponse 允许自定义原始的响应数据(字符串)
  transformResponse: [function (data) {
    try {
      // 如果转换成功则返回转换的数据结果
      return JSONBig.parse(data)
    } catch (err) {
      // 如果转换失败,则包装为统一数据格式并返回
      return {
        data
      }
    }
  }]
})

export default request

其他地方不需要改动,这个时候前后端数据交互时id参数传输的时候会自动转化为字符串类型传输{id: " 33978617558956897  "}。


本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://shenqiku.cn/article/FLY_11691