SpringBoot与Vue前后分离项目中返回结果为Long类型精度丢失问题处理
文章目录
- SpringBoot与Vue前后分离项目中返回结果为Long类型精度丢失问题处理
- 1. 前后分离环境
- 1. 表结构
- 2. 实体类
- 2. 问题描述
- 3. 原因分析
- 4. 处理方法(后端)
- 1. 处理方式1(@JsonSerialize注解)
- 2. 处理方式2(@JsonFormat注解)
- 3. 处理方式3推荐(全局解决)
- 5. 处理方法(前端)
- 1. 安装 json-bigint
- 2. Axios处理
- 6. 处理后输出结果如下
1. 前后分离环境
后端: SpringBoot + Mybatis-plus
前端: vue + elemnet-ui
数据库:Oracle12c
1. 表结构
create table TB_CONFIG
(
ID NUMBER(20) not null primary key,
MODULE_NAME VARCHAR2(100),
CONFIG CLOB,
NOTE VARCHAR2(200),
CREATED_BY NUMBER(20),
CREATE_TIME DATE default SYSDATE
)
2. 实体类
package com.yuan.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@TableName("TB_CONFIG")
@ApiModel(value = "Config对象", description = "配置表")
public class Config implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "ID")
@TableId
private Long id;
@ApiModelProperty(value = "模块")
private String moduleName;
@ApiModelProperty(value = "配置项")
private String config;
@ApiModelProperty(value = "备注")
private String note;
@ApiModelProperty(value = "创建时间")
private LocalDateTime createTime;
}
2. 问题描述
数据库中存储的是Nubmer(20)类型的数据,前端接收后精度丢失
- 数据库记录
- 前端接收到的结果如下,最后两位会变成
00
[
{
"id": 1779709862042493000,
"moduleName": "顶顶顶顶",
"config": null,
"note": null,
"createTime": "2024-04-15T11:19:59",
},
{
"id": 1779709995329085400,
"moduleName": "222",
"config": null,
"note": null,
"createTime": "2024-04-15T11:19:59",
},
{
"id": 1779711237837119500,
"moduleName": "3",
"config": null,
"note": null,
"createTime": "2024-04-15T11:19:59",
},
{
"id": 1778942074839457800,
"moduleName": "jinshengyuan",
"config": null,
"note": "备注",
"createTime": "2024-04-13T08:23:36",
}
]
3. 原因分析
MyBatis-Plus中使用 ASSIGN_ID(默认雪花算法) 生成的id作为主键时(或使用其他工具,如
hutool
生成雪花算法的ID值时),因为其长度为19位,而前端一般能处理16位,所以结果返回给前端会造成精度丢失,最后两位会变成00
,有点类似四舍五入的效果
4. 处理方法(后端)
Spring Boot默认使用的是
jackson
对响应的JSON数据进行序列化的,后端处理方式就是返回结果给前端时,将Long类型转换为字符串即可解决,如果改变了默认的序列化框架jackson
为其他框架,则需要按照对应框架的相关规则来处理,如fastjson
等。下面以jackson
序列化为例。
- 局部解决: 通过在字段上标注
@JsonSerialize
或@JsonFormat
注解来解决- 全局解决:通过配置类,全局转换
1. 处理方式1(@JsonSerialize注解)
给需要转换的Long类型字段上面标注
@JsonSerialize(using = ToStringSerializer.class)
来解决,如下
//......
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
//......
public class Config implements Serializable {
private static final long serialVersionUID = 1L;
@JsonSerialize(using = ToStringSerializer.class)//Long类型转String,解决前端精度丢失问题
@ApiModelProperty(value = "ID")
@TableId
private Long id;
//......
}
2. 处理方式2(@JsonFormat注解)
给需要转换的Long类型字段上面标注
@JsonSerialize(using = ToStringSerializer.class)
来解决,如下
//......
import com.fasterxml.jackson.annotation.JsonFormat;
//......
public class Config implements Serializable {
private static final long serialVersionUID = 1L;
@JsonFormat(shape = JsonFormat.Shape.STRING)//Long类型转String,解决前端精度丢失问题
@ApiModelProperty(value = "ID")
@TableId
private Long id;
//......
}
3. 处理方式3推荐(全局解决)
新增一个配置类,然后配置需要转换序列化的类型即可,如下
package com.yuan.config;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.math.BigInteger;
/**
* <p>
*
* @Description: 解决数据库主键存储为雪花算法生成的ID值后,返回给前端时精度丢失的问题 <br>
* </p>
* @Datetime: 2024/4/14 16:33
* @Author: Yuan · JinSheng <br>
* @Since 2024/4/14 16:33
*/
@Configuration
public class Jackson2ObjectConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
return builder -> {
builder.serializerByType(Long.class, ToStringSerializer.instance);
builder.serializerByType(BigInteger.class, ToStringSerializer.instance);
//其他类型转换可以按需在这里添加......
};
}
}
5. 处理方法(前端)
前端可借助
json-bigint
插件来解决,一般请求都使用的Axios库,所以可以通过axios.defaults.transformResponse
结合jsono-bigint
来解决
1. 安装 json-bigint
pnpm add json-bigint
# 或
yarn add json-bigint
2. Axios处理
//......
import JSONBIG from 'json-bigint'
//......
axios.defaults.transformResponse = [
function (data) {
const jsonBig = JSONBIG({
storeAsString: true
})
return jsonBig.parse(data)
}
]
//......
6. 处理后输出结果如下
可看到id已经被转换为字符串了
[
{
"id": "1779709862042492928",
"moduleName": "顶顶顶顶",
"config": null,
"note": null,
"createTime": "2024-04-15T11:14:31"
},
{
"id": "1779709995329085440",
"moduleName": "222",
"config": null,
"note": null,
"createTime": "2024-04-15T11:15:02"
},
{
"id": "1779711237837119488",
"moduleName": "3",
"config": null,
"note": null,
"createTime": "2024-04-15T11:19:59"
},
{
"id": "1778942074839457792",
"moduleName": "jinshengyuan",
"config": "{}",
"note": "备注",
"createTime": "2024-04-13T08:23:36"
}
]