NUMBER类型本身不丢精度,MyBatis“看似丢失”实为映射链路中误用浮点数或未规整BigDecimal的scale;应始终用getBigDecimal()、显式配置jdbcType="NUMERIC"与javaType="java.math.BigDecimal",并统一通过TypeHandler规范scale。
number 类型本身不会丢精度,mybatis 读取时“看起来丢了”,本质是类型映射链路上某环用了浮点数或未规整 bigdecimal 的 scale。
MyBatis 默认用 getDouble() 或 getFloat() 读取 NUMBER?
常见错误:在 ResultSet 中手动调用 rss.getDouble("amount") 或 rss.getFloat("id")。
Oracle 的 NUMBER 可能存 19 位整数(如身份证号、订单号)或 38 位十进制小数(如金融金额),而 double 最多精确到 15–16 位有效数字。一旦超过,就会四舍五入或科学计数法表示。
-
NUMBER(19,0)存1234567890123456789→getDouble()返回1.2345678901234567E18→ 转字符串变成"1234567890123456800" -
NUMBER(10,2)存9999999.99→getFloat()可能返回1.0E7,直接崩
正确做法永远只用 getBigDecimal(),哪怕字段定义是 NUMBER(5,0)。
MyBatis 的 jdbcType=NUMBER 没配 javaType,导致自动 fallback 到 Integer 或 Long?
这是隐性陷阱。MyBatis 在没显式声明 javaType 时,会根据 JDBC 驱动返回的类型推测 Java 类型:
- 若 Oracle 返回的是
oracle.jdbc.driver.OracleNumber,MyBatis 可能尝试转成Long - 但
Long范围是 ±9,223,372,036,854,775,807(19 位),而NUMBER(20,0)可存 20 位整数 → 溢出变负数或静默截断 - 更糟的是:空值(
NULL)可能被填成0而非null,尤其在BigDecimal字段声明为非包装类(如BigDecimal amount;而非BigDecimal amount;)时
务必在 <resultmap></resultmap> 中显式写:
<result column="amount" property="amount" jdbcType="NUMERIC" javaType="java.math.BigDecimal"/>注意用
jdbcType="NUMERIC"(而非 "NUMBER"),因为 Oracle 驱动对两者的 type code 处理略有差异,NUMERIC 更稳定触发 BigDecimal 映射。
BigDecimal 的 scale 不一致,导致比较/展示/JSON 序列化出错?
JDBC 驱动返回的 BigDecimal 的 scale 不一定等于数据库定义的 s(比如 NUMBER(10,2) 存 100,getBigDecimal() 可能返回 100(scale=0),而非 100.00(scale=2))。
这会导致:
- 前端 JSON 序列化时输出
100而非100.00,金额展示缺零 -
compareTo()误判:new BigDecimal("100").compareTo(new BigDecimal("100.00")) == 0是 true,但若业务强依赖 scale(如校验固定两位小数),就出问题 - MyBatis 的
BeanPropertyRowMapper不做 scale 规整,靠你代码补
解决方案不是“还原数据库 scale”,而是按业务语义主动设:
- 金额字段:一律
bigDecimal.setScale(2, RoundingMode.HALF_UP) - ID 字段:若确定无小数,可用
bigDecimal.setScale(0, RoundingMode.UNNECESSARY)(失败则抛异常) - 避免
new BigDecimal(double)—— double 已失真,再包一层毫无意义
自定义 TypeHandler 是最稳妥的统一控制点
手写一个继承 BigDecimalTypeHandler 的类,重写 getNullableResult,强制所有 NUMERIC 列返回带业务 scale 的 BigDecimal:
public class MoneyTypeHandler extends BigDecimalTypeHandler {
@Override
public BigDecimal getNullableResult(ResultSet rs, String column) throws SQLException {
BigDecimal val = super.getNullableResult(rs, column);
return val == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : val.setScale(2, RoundingMode.HALF_UP);
}
}然后在 MyBatis 配置中注册:
<typeHandlers> <typeHandler handler="com.example.MoneyTypeHandler" jdbcType="NUMERIC"/> </typeHandlers>
这样连 XML 映射里都不用反复写 javaType,所有 numeric 列自动按规则处理。
真正容易被忽略的,是 scale 不一致带来的隐性 bug —— 它不报错,只是 equals() 返回 false、前端少两个零、金额对账差一分钱。这类问题往往在线上跑一周才暴露。


















