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

Hibernate+H2数据库因Schema创建方式导致BigDecimal返回值不一致

这个问题我之前也碰到过,本质是Hibernate和Liquibase生成的H2字段定义虽然表面参数一致,但实际底层有差异,或者H2返回的BigDecimal的scale不同导致字符串表现不一样。给你几个可行的解决方向:

1. 先确保Schema完全一致(最根本的解决办法)

你提到实体字段标注的是precision = 4, scale = 16,这里要注意:H2的DECIMAL(p,s)要求总精度p必须大于等于小数位s,否则H2会自动调整p的值(比如把p设为s+1)。而Hibernate在生成Schema时,可能会自动修正这个不合理的参数(比如把p调整为20),但Liquibase会严格按照你写的参数生成,导致两者实际的字段定义不一样。

解决步骤:

  • 启动用Hibernate create-drop的应用,打开H2控制台查看表的字段类型,比如实际生成的是DECIMAL(20,16)还是其他。
  • 在Liquibase的changelog里,把对应的字段precision改成和Hibernate生成的一致,比如如果是20,就写<column name="xxx" type="DECIMAL(20,16)"/>或者用precision="20" scale="16"。
    这样两者的Schema完全一致,返回的BigDecimal格式自然就统一了。
2. 用AttributeConverter统一处理BigDecimal格式

如果不想调整Schema,可以通过Hibernate的属性转换器,把读取到的BigDecimal统一转成相同的格式:

写一个全局生效的转换器:

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.math.BigDecimal;

@Converter(autoApply = true)
public class BigDecimalNormalizer implements AttributeConverter<BigDecimal, BigDecimal> {

    @Override
    public BigDecimal convertToDatabaseColumn(BigDecimal attribute) {
        return attribute;
    }

    @Override
    public BigDecimal convertToEntityAttribute(BigDecimal dbData) {
        if (dbData == null) {
            return null;
        }
        // 转成最简形式,去掉末尾的0和多余的小数点
        return dbData.stripTrailingZeros();
    }
}

这个转换器会自动把所有BigDecimal类型的字段转成最简格式,不管数据库返回的是420还是420.000000000000,最终实体里的BigDecimal调用toString()后都会是"420"。如果想统一保留小数位,把stripTrailingZeros()改成dbData.setScale(16)即可。

3. 调整测试断言逻辑(最快的临时解决)

如果只是测试需要一致,也可以不修改代码或Schema,而是调整测试里的断言方式:
不要直接断言返回的字符串是"420",而是断言BigDecimal的数值相等。比如用assertEquals(0, new BigDecimal("420").compareTo(返回的BigDecimal))——因为BigDecimal.compareTo()只比较数值,不考虑scale;而equals()会同时比较数值和scale,这也是之前测试失败的原因之一。

这样不管返回的是420还是420.000000000000,只要数值正确,测试就会通过。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 07:47:28