【踩坑经历】Java Long 类型传给前端损失精度的问题

2023-11-15

最近在做一个 SpringBoot + Vue 的项目,持久层框架用的是 MyBatis-Plus,然后遇到了一个问题,一起来看下怎么回事。

这个项目就是一个文章收藏器,可以收藏一些技术文章,然后可以选择星标,以便查找这篇文章。
在这里插入图片描述
那么点击星标的按钮,实际上就是调了后端一个接口,更新了数据库中相应字段。每一个列表的字段如下:
在这里插入图片描述
可以看到,标记星标的字段就是 isFavorite ,没有星标的时候是 0 ,星标之后变成 1 。同时这边还有个 id 字段,这个字段是由 MyBatis-Plus 插入数据库时自动填充的,是一个 Long 类型的 uuid 。在调接口的时候,后台根据传过来的 id 进行条件更新。

但是在测试的时候,调接口发现 isFavorite 一直更新失败,但是SQL 语句正常执行,没有报错,看一下打印的 SQL 语句:
在这里插入图片描述
然后 Controller 层的逻辑是这样写的:

@PutMapping
public ResponseEntity<ServerResponse> changeFavoriteStatus(
        @NotBlank(message = "文章 ID 不能为空") @RequestParam("articleId") String articleId,
        @NotBlank(message = "星标状态不能为空") @RequestParam("isFavorite") String isFavorite
) {
	LambdaUpdateWrapper<ArticleModel> updateWrapper = new LambdaUpdateWrapper<>();
    updateWrapper
            .eq(ArticleModel::getId, Long.valueOf(articleId))
            .set(ArticleModel::getIsFavorite, Integer.valueOf(isFavorite));
	articleMapper.update(null, updateWrapper);
    return ServerResponse.ok(null);
}

这就有点头疼了,关键没有报错,无从下手啊。假如是 MyBatis-Plus 提供的 API 有问题,应该会有报错信息。然后网上查了很多资料,看别人的代码都是跟我一样写的,但是也没看他们遇到这样的问题。

后来尝试硬编码,直接在代码里面填写参数,发现可以更新成功:

@PutMapping
public ResponseEntity<ServerResponse> changeFavoriteStatus(
        @NotBlank(message = "文章 ID 不能为空") @RequestParam("articleId") String articleId,
        @NotBlank(message = "星标状态不能为空") @RequestParam("isFavorite") String isFavorite
) {
	LambdaUpdateWrapper<ArticleModel> updateWrapper = new LambdaUpdateWrapper<>();
    updateWrapper
            .eq(ArticleModel::getId, 1404788380714967042L)
            .set(ArticleModel::getIsFavorite, 1);
	articleMapper.update(null, updateWrapper);
    return ServerResponse.ok(null);
}

这样一下就有了思路,因为我这边的 id 是直接数据库复制过来的,那么基本可以确定是前端传过来的 id 有问题。看一下查询接口打印的 SQL 语句:
在这里插入图片描述
看到第一条记录的 id 字段是 1404788380714967042 。然后再来看一下传给前端的字段,发现确实不一样:
在这里插入图片描述
综上,本人再查询了相关资料,Java 中 long 数据类型是 64 位,最大值是 9,223,372,036,854,775,807 ,也就是 2^63 - 1 ,然后 JavaScript 中的 number 类型,最大的安全整数 2^53 - 1

也就是说,Java 中的 Long 类型能表示的范围比 JS 的 number 类型大,如果后端在序列化的时候直接用 Long 类型,前端在反序列化的时候,number 会损失精度,导致 id 字段不是实际的 id ,进而查询不到记录,无法更新。因此对于 Long 类型,后端需要转为字符串才能传给前端。

转为字符串的话,常规的做法是定义一个 DTO 类,将 Long 类型的字段都定义成 String ,那么这样的做法比较麻烦,而且如果接口有很多,需要大量的 DTO 。另一种做法是使用注解,在序列化的时候保留精度。

SpringBoot 默认使用的 JSON 解析框架是 jackson,可以直接使用。如果要用第三方框架,例如 fastjson 则需要进行配置。以 jackson 为例,对于在序列化时需要保留精度的字段,添加 @JsonSerialize 注解即可:

@Data
@TableName("tb_article")
public class ArticleModel {
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;

    private Integer isFavorite;
}

在添加注解之后,前端拿到的参数如下,可以看到 Long 类型 id 变成字符串了:
在这里插入图片描述
另外这边还有一个时间戳,没有转为字符串,本人认为没有必要,因为 JS 里面本身也有时间戳哈,而且这个范围没有超出 number 的最大安全整数,可以直接用。另外 jackson 还提供了注解可以在序列化时自定义格式化:

@Data
//序列化、反序列化忽略的属性,多个时用“,”隔开
@JsonIgnoreProperties({"captcha"})
//当属性的值为空(null或者"")时,不进行序列化,可以减少数据传输
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class UserVoByJson {

    // 序列化、反序列化时,属性的名称
    @JsonProperty("userName")
    private String username;

    // 为反序列化期间要接受的属性定义一个或多个替代名称,可以与@JsonProperty一起使用
    @JsonAlias({"pass_word", "passWord"})
    @JsonProperty("pwd")
    private String password;

    //序列化、反序列化时,格式化时间
    @JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createDate;

    //序列化、反序列化忽略属性
    @JsonIgnore
    private String captcha;

}

参考:
java long类型报错:error: integer number too large
java的long类型传输到前端损失精度
Springboot引入FastJson
修复Long类型太长,而Java序列化JSON丢失精度问题的方法
Number.MAX_SAFE_INTEGER - MDN
SpringBoot系列——Jackson序列化

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【踩坑经历】Java Long 类型传给前端损失精度的问题 的相关文章

  • 为什么 JTables 使 TableModel 在呈现时不可序列化?

    所以最近我正在开发一个工具 供我们配置某些应用程序 它不需要是什么真正令人敬畏的东西 只是一个具有一些 SQL 脚本生成功能并创建几个 XML 文件的基本工具 在此期间 我使用自己的 AbstractTableModel 实现创建了一系列
  • 动态选择端口号?

    在 Java 中 我需要获取端口号以在同一程序的多个实例之间进行通信 现在 我可以简单地选择一些固定的数字并使用它 但我想知道是否有一种方法可以动态选择端口号 这样我就不必打扰我的用户设置端口号 这是我的一个想法 其工作原理如下 有一个固定
  • 过滤两次 Lambda Java

    我有一个清单如下 1 2 3 4 5 6 7 和 预期结果必须是 1 2 3 4 5 6 7 我知道怎么做才能到7点 我的结果 1 2 3 4 5 6 我也想知道如何输入 7 我添加了i gt i objList size 1到我的过滤器
  • Pig Udf 显示结果

    我是 Pig 的新手 我用 Java 编写了一个 udf 并且包含了一个 System out println 其中的声明 我必须知道在 Pig 中运行时该语句在哪里打印 假设你的UDF 扩展了 EvalFunc 您可以使用从返回的 Log
  • 谷歌应用程序引擎会话

    什么是java应用程序引擎 默认会话超时 如果我们将会话超时设置为非常非常长的时间 会不会产生不良影响 因为谷歌应用程序引擎会话默认情况下仅存储在数据存储中 就像facebook一样 每次访问该页面时 会话仍然永远存在 默认会话超时设置为
  • 从最终实体获取根证书和中间证书

    作为密码学的菜鸟 我每天都会偶然发现一些简单的事情 今天只是那些日子之一 我想用 bouncy castle 库验证 java 中的 smime 消息 我想我几乎已经弄清楚了 但此时的问题是 PKIXparameters 对象的构建 假设我
  • 将流转换为 IntStream

    我有一种感觉 我在这里错过了一些东西 我发现自己做了以下事情 private static int getHighestValue Map
  • 检测并缩短字符串中的所有网址

    假设我有一条字符串消息 您应该将 file zip 上传到http google com extremelylonglink zip http google com extremelylonglink zip not https stack
  • 将 MOXy 设置为 JAXB 提供程序,而在同一包中没有属性文件

    我正在尝试使用 MOXy 作为我的 JAXB 提供程序 以便将内容编组 解组到 XML JSON 中 我创建了 jaxb properties 文件 内容如下 javax xml bind context factory org eclip
  • 帮助将图像从 Servlet 获取到 JSP 页面 [重复]

    这个问题在这里已经有答案了 我目前必须生成一个显示字符串文本的图像 我需要在 Servlet 上制作此图像 然后以某种方式将图像传递到 JSP 页面 以便它可以显示它 我试图避免保存图像 而是以某种方式将图像流式传输到 JSP 自从我开始寻
  • jdbc mysql loginTimeout 不起作用

    有人可以解释一下为什么下面的程序在 3 秒后超时 因为我将其设置为在 3 秒后超时 12秒 我特意关闭了mysql服务器来测试mysql服务器无法访问的这种场景 import java sql Connection import java
  • Spring Boot Data JPA 从存储过程接收多个输出参数

    我尝试通过 Spring Boot Data JPA v2 2 6 调用具有多个输出参数的存储过程 但收到错误 DEBUG http nio 8080 exec 1 org hibernate engine jdbc spi SqlStat
  • 在我的 Spring Boot 示例中无法打开版本 3 中的 Swagger UI

    我在 Spring Boot 示例中打开 swagger ui 时遇到问题 当我访问 localhost 8080 swagger ui 或 localhost 8080 root api name swagger ui 时出现这种错误 S
  • 不接受任何内容也不返回任何内容的函数接口[重复]

    这个问题在这里已经有答案了 JDK中是否有一个标准的函数式接口 不接受也不返回任何内容 我找不到一个 像下面这样 FunctionalInterface interface Action void execute 可运行怎么样 Functi
  • 关键字“table”附近的语法不正确,无法提取结果集

    我使用 SQL Server 创建了一个项目 其中包含以下文件 UserDAO java public class UserDAO private static SessionFactory sessionFactory static se
  • Eclipse 启动时崩溃;退出代码=13

    I am trying to work with Eclipse Helios on my x64 machine Im pretty sure now that this problem could occur with any ecli
  • 我如何在java中读取二进制数据文件

    因此 我正在为学校做一个项目 我需要读取二进制数据文件并使用它来生成角色的统计数据 例如力量和智慧 它的设置是让前 8 位组成一个统计数据 我想知道执行此操作的实际语法是什么 是不是就像读文本文件一样 这样 File file new Fi
  • 使用 svn 1.8.x、subclise 1.10 的 m2e-subclipse 连接器在哪里?

    我读到 m2e 的生产商已经停止生产 svn 1 7 以外的任何版本的 m2e 连接器 Tigris 显然已经填补了维护 m2e subclipse 连接器的空缺 Q1 我的问题是 使用 svn 1 8 x 的 eclipse 更新 url
  • 通过浏览器关闭页面时出现 Websocket 错误:“已建立的连接被主机中的软件中止”

    我开发了一个实时通知系统Spring 4 代码可以在 Github 上找到 github com vdenotaris Spring Messaging https github com vdenotaris Spring Messagin
  • Spring Rest 和 Jsonp

    我正在尝试让我的 Spring Rest 控制器返回jsonp但我没有快乐 如果我想返回 json 但我有返回的要求 完全相同的代码可以正常工作jsonp我添加了一个转换器 我在网上找到了用于执行 jsonp 转换的源代码 我正在使用 Sp

随机推荐

  • C语言【二分查找】详解

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 二分 文章目录 前言 一 二分查找 的步骤 二 二分查找 的注意事项 三 举例详解 二分查找 前言 一 二分查找 步骤 二 二分查找 的注意事项 三 举例详细讲解 二分查找
  • redis使用rightPushAll保存List,List的size为1

    redis使用rightPushAll保存List List的size为1 问题描述 问题定位 解决方案 问题疑惑 问题描述 使用RedisTemplate保存List 期望是直接将Java的List直接转换redis的list 但是rig
  • C++智能指针简介

    C 智能指针简介 一 简介 前言 从C到C C malloc 和 free C new 和 delete 从C 到Morden C C new 和 delete morden C shared ptr unique ptr 和 weak p
  • IDEA创建Zookeeper客户端

    IDEA创建Zookeeper客户端 解决单机Zookeeper的错误 不是Zookeeper集群问题 但是集群问题也可以参考 网上看到的教程基本上都是服务器端口号 防火墙的问题 但是个人测试之后发现没有解决问题 于是自己尝试一下方法 最后
  • 梯度下降法的推导(非常详细、易懂的推导)

    原作者 红色石头 来自 AI有道 梯度下降 算法的公式非常简单 沿着梯度的反方向 坡度最陡 是我们日常经验得到的 其本质的原因到底是什么呢 为什么局部下降最快的方向就是梯度的负方向呢 也许很多朋友还不太清楚 没关系 接下来我将以通俗的语言来
  • WSL2开启后Vmware虚拟机性能下降

    开启WSL2 需要开启 虚拟机平台 功能 实际就是HyperV 开启后 Vmware虚拟机里面性能下降大约10 再次断绝了我的WSL2直接挂在ext分区的念想 见图 开启前 CPU Z跑分464 开启后414 按照Vmware建议关闭侧信道
  • C++学习(六十六)IoT和AIoT

    loT是物联网的英文名称 是Internet of Things的缩写 AloT AI IoT 是人工智能 物联网的英文名称 智能物联网
  • 修复ROS2使用zsh无法用tab补全 ros2 指令的相关问题

    安装完ROS2 后 改用zsh发现无法使用tab补全 ros2相关指令 现记录下修复办法 首先 安装python3 argcomplete sudo apt install python3 argcomplete 然后 在 zshrc里面添
  • STM32 CAN通信的学习笔记总结(从小白开始)

    知识来源于互联网 回馈于互联网 目录 1 总体概述 1 1 基本概念 1 2 通讯方式 1 3 为什么使用CAN 1 4 CAN的协议及组成 2 上帝视角看CAN的通讯过程 2 1 数据传输原理实现 2 2 通信的整个过程 2 2 1 空闲
  • Python读写excel文件

    1 使用pandas库读取Excel 最常用 pandas可以读取各种各样格式的数据文件 一般输出dataframe格式 如 txt csv excel json 剪切板 数据库 html hdf parquet pickled文件 sas
  • FSCapture注册码

    企业版序列号 name bluman serial 序列号 注册码 VPISCJULXUFGDDXYAUYF 转载于 https www cnblogs com wshsdlau p 4396184 html
  • HTML<DIV>常用标签

    目录 1 什么是DIV 1 1 div是什么意思 1 2 div标签怎么用 1 3 div布局优势 1 4 DIV作用是什么 1 5 有哪些DIV方式 1 5 1行内样式 1 5 2内嵌样式 1 5 3外部样式 1 6 样式使用规则 2 D
  • 使用队列实现stack

    两个队列实现一个stack q1只保持一个元素即可 多余的转换到q2当中 出队列元素 有两种情况 q1不为空 直接出队列 如果连续出队列 q1可能为空 需要q2的部分元素放到q1当中去 说白了就是元素捣鼓来捣鼓去的问题即可 class My
  • Linux-安装redis6.2.1及主备复制模式(replication)

    Linux 安装redis6 2 1 下载redis6 2 1资源 上传至安装目录 解压及编译 解压 修改名称 编译 修改配置文件 主节点 从节点 启动及测试 启动 主节点 从节点 测试 下载redis6 2 1资源 地址 https re
  • 华为数通方向HCIP-DataCom H12-821题库(单选题:101-120)

    第101题 可用于多种路由协议 由 if match 和 apply 子句组成的路由选择工具是 A route policy B IP Prefix C commnityfilter D as path filter 答案 A 解析 Rou
  • QT对话框去掉帮助和关闭按钮 拦截QT关闭窗口的CloseEvent

    建了一个对话框 我不想把边框去掉 只想去掉关闭按钮 setWindowFlags windowFlags Qt WindowCloseButtonHint Qt WindowContextHelpButtonHint 结果那个问号的按钮去掉
  • c++序列化以及反序列化实现

    1 什么是序列化和反序列化 当我们在写程序时 比如说我们自定义了一个实体类Person 然后在程序中创建一个该实体类对象 并给对象赋了一些值 但是我们想将这些数据发给我们的其他的程序员朋友 让他们也可以调用我们创建的这个实体类并使用我们的数
  • 数据库实时同步利器——CDC(变化数据捕获技术)

    在进行数据ETL过程中 我们经常需要通过周期性的定时调度将业务数据按照T 1的方式同步到数据仓库中 进行数据分析处理 最终通过BI报表展示给最终用户 但这种方式实时性较差 用户往往只能看到昨天的数据 会影响用户决策的及时性 而如果用户要近实
  • 更换持续集成工具,从 Travis 到 Github Actions

    我真傻 真的 单单受文档的推荐就选择了 Travis 作为部分项目的持续集成工具 没有料到它早已于 2020 年 12 月更换了免费政策 不再为开源项目提供免费的用于持续集成使用的 Credits 了 当赠送的 10000 个点数用完 就需
  • 【踩坑经历】Java Long 类型传给前端损失精度的问题

    最近在做一个 SpringBoot Vue 的项目 持久层框架用的是 MyBatis Plus 然后遇到了一个问题 一起来看下怎么回事 这个项目就是一个文章收藏器 可以收藏一些技术文章 然后可以选择星标 以便查找这篇文章 那么点击星标的按钮