You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MyBatis插入Double类型数据时丢失精度问题求助

问题描述

MyBatis映射器里的批量插入SQL如下:

<foreach collection="ps" item="p" separator=";" open="" close="">
    INSERT INTO ${stageTableName} VALUES(
        #{p.v1},
        #{p.v2},
        #{p.v3},
        #{p.v4},
        #{p.v5},
        #{vx},
        #{p.quantity}
    )
    ON CONFLICT ("v1", "v2", "v3", "v4", "v5")
    DO UPDATE SET quantity = ${stageTableName}.quantity + EXCLUDED.quantity
</foreach>

ps是Java对象列表,之前运行都正常,但最近碰到个问题:当p.quantity的值是1222371636058.8174时,数据库存下来变成了1222371636058.82,精度丢了。

已排查信息

  • 调试确认精度丢失是在MyBatis插入环节发生的
  • 直接把这个数值硬编码到SQL里执行,Postgres能正确存储1222371636058.8174
  • 只有这种大数值会出问题,像123.8124872这类小数值存储精度完全正常

环境信息

  • Java对象中p.quantity的类型:Double
  • 数据库quantity字段类型:NUMERIC(38,7)
  • 数据库:Postgres

原因分析

问题根源在于Java的Double类型没办法精确表示所有十进制小数。Double是遵循IEEE 754标准的双精度浮点数,它用二进制存储数值,当十进制数的整数部分位数较多时(比如你这个案例里整数部分有12位),剩下的二进制位数不足以精确存储所有小数位。1222371636058.8174这个值在Double里实际存的是近似值,刚好就是1222371636058.82左右,MyBatis把这个近似值传给数据库,自然就丢了精度。

小数值的整数部分位数少,二进制有足够空间存下所有小数细节,所以不会出问题。另外MyBatis处理Double参数时,直接传的是浮点数的近似值,不会自动转成高精度十进制类型,这就把Double的精度缺陷放大了。


解决办法

1. 改Java对象字段类型(最优方案)

把p.quantity的类型从Double换成java.math.BigDecimal,这是Java专门用来精确表示十进制数的类型,能完整保留所有小数位的信息,从根源上解决精度问题。

2. 适配MyBatis配置

改完Java类型后,MyBatis默认支持BigDecimal参数绑定,会自动把它以高精度十进制类型传给Postgres,完美匹配NUMERIC(38,7)的字段类型,原SQL里的#{p.quantity}不用做任何改动。

临时规避方案(不推荐)

如果暂时没法改Java类型,可以在MyBatis映射里显式指定参数的JDBC类型为DECIMAL,强制MyBatis把Double转成高精度十进制再传递:

#{p.quantity, jdbcType=DECIMAL}

但这种方式只是临时救急,因为Double本身已经存在精度损失了,不如直接换BigDecimal彻底解决问题。


内容的提问来源于stack exchange,提问作者Ram

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 05:35:18