java传输Long类型精度损失

探讨在MybatisPlus中使用雪花算法生成Long类型ID,Swagger显示精度丢失的问题及解决方案。通过修改Spring Boot配置,将Long类型转换为字符串,避免精度损失。

起因

在使用Mybatis Plus的雪花算法生成Long类型id时发现Swagger返回id与数据库的id不一致,但直接访问URL接口时返回的id却是正确的,即数据库id与URL访问返回的id一致,仅Swagger不一致,也许这就是买家秀(Swagger)与卖家秀(数据库,接口URL),至于原因嘛…
宇宙尽头
百度一下,结论为:js的number最大值为9007199254740992,所以swagger的显示也会出错。如下2图,Swagger显示的是错误的id,url直接请求则返回正确的。
在这里插入图片描述在这里插入图片描述

百度解决方案

  • application.yml添加以下jackson配置数字类型都转为字符串返回:

    spring:
      jackson:
        generator:
          write_numbers_as_strings: true
    

    但其实我只想处理Long类型,都转字符串似乎有点隆重了。

  • 注解

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

    麻烦。

  • 自定义ObjectMapper

      @Bean("jackson2ObjectMapperBuilderCustomizer")
      public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
          Jackson2ObjectMapperBuilderCustomizer customizer = new Jackson2ObjectMapperBuilderCustomizer() {
              @Override
              public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
                  jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance)
                          .serializerByType(Long.TYPE, ToStringSerializer.instance);
              }
          };
          return customizer;
      }
    

    这个方案已经可以符合我的目的了:Long精度不损失、数字类型只有Long转为字符串。但这个方案看起来好像还是有点麻烦,于是我便根据这个方案改成个人认为观感更简单的配置。

个人解决方案

在Spring容器中,大部分bean都是以单例形式存在的,只要知道这点便可推知如果我们想更改一些bean默认的全局配置,我们只需将所需bean取出来进行更改即可。根据这点我对自定义ObjectMapper的解决方案优化成以下这个样子:

@Configuration
@Slf4j
public class WebConfig {
    @Resource
    private ObjectMapper objectMapper;

    @PostConstruct
    public void init() {
        /*
         * 序列换成json时,将long转string避免精度丢失
         */
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        objectMapper.registerModule(simpleModule);
        log.info("WebConfig init complete");
    }
}

SpringBoot的bean初始化完成后容器就会有全局默认的ObjectMapper bean,如果我们想更改全局序列化的方式,只需对这个bean动手就行。更改后在Swagger重新测试,成功转成字符串且避免精度损失,结果如下图:
在这里插入图片描述

### Java前后端Long类型精度丢失解决方案 在Java与前端交互过程中,由于JavaScript的`Number`类型的数值范围有限(约为±(2^53)-1),而Java的`Long`类型可以表示更大的整数范围(约±(2^63)-1)。因此,在前后端递大数值容易发生精度丢失问题。 以下是几种常见的解决方案: #### 方案一:后端将Long类型转为String类型发送到前端 通过修改后端代码,将`Long`类型的字段序列化为字符串形式后再递至前端。这样可以有效避免因数值过大而导致的精度丢失问题[^1]。 ```java import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; @Data @TableName("ap_article") public class ApArticle implements Serializable { @TableId(value = "id", type = IdType.ID_WORKER) @JsonSerialize(using = ToStringSerializer.class) // 将字段转换为字符串进行响应 private Long id; } ``` 此方法利用Jackson库中的`ToStringSerializer`实现自动转换功能[^5]。 --- #### 方案二:前端使用BigInt替代Number类型 对于现代浏览器环境下的应用开发,可以通过让前端采用`BigInt`来代替统的`Number`类型处理大数据量场景下可能出现的问题[^4]。需要注意的是该特性仅适用于支持ES2020及以上版本标准的新一代Web客户端程序设计框架之中。 例如: ```javascript let bigIntValue = BigInt('1435421253099634623'); console.log(bigIntValue.toString()); // 输出完整的原始值 ``` 这种方式要求服务端仍然保持原有的数据结构不变,只是调整了客户端解析逻辑[^2]。 --- #### 方案三:统一约定并限制ID长度 如果项目允许的话,也可以考虑重新规划业务模型中的唯一标识符生成策略,比如缩短其位宽或者选用其他更适合跨平台共享的数据格式作为主键定义依据之一[^3]。不过这种方法通常会带来额外的工作负担以及潜在的风险因素,故需谨慎评估实施成本效益比之后再做决定。 --- #### 总结 以上三种方式各有优劣之处,具体选择哪一种取决于实际应用场景需求和技术栈现状等因素综合考量结果得出结论。一般推荐优先尝试第一种做法即借助JSON序列化工具包完成自动化映射操作最为简便高效;而对于那些追求极致性能优化或者是老旧系统升级改造工程,则可能需要结合第二种甚至第三种手段共同作用才能达到理想效果。 ```python # 示例Python伪代码展示如何验证上述理论正确性的简单测试脚本 def test_long_precision(): large_number_str = '1435421253099634623' try: # JavaScript Number 类型无法精确存储这个数字 number_value = float(large_number_str) print(f'Float representation: {number_value}') # 使用 Python 的 int 或者 str 来保留原样 exact_representation_via_int = int(large_number_str) print(f'Exact via Int: {exact_representation_via_int}') exact_representation_via_string = large_number_str print(f'Exact via String: "{exact_representation_via_string}"') except Exception as e: print(e) test_long_precision() ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值