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

如何通过Hibernate或Spring Data设置存储过程输出参数BigDecimal的小数位数?

问题描述

在JDBC中,可通过以下方式设置DECIMAL类型输出参数的小数位数(scale):

callableStatement.registerOutParameter(4, Types.DECIMAL, 34);

根据Java文档,此处的34即为scale,表示小数点后保留的位数。

但在Hibernate中,无论是使用StoredProcedureQuery API还是@NamedStoredProcedureQuery注解,都找不到直接指定scale的入口:

  • API方式:
StoredProcedureQuery query = em.createStoredProcedureQuery("test")
        .registerStoredProcedureParameter(
                "aDecimal",
                BigDecimal.class,
                ParameterMode.OUT
        ); // 无scale参数
  • 注解方式:
@NamedStoredProcedureQuery(name = "blah.test", procedureName = "test", parameters = {
        @StoredProcedureParameter(mode = ParameterMode.OUT, name = "aDecimal", type = BigDecimal.class), // 无scale元素
})

环境说明:使用SQL Server 2019,存储过程输出参数类型为DECIMAL(38,34)。

解决方案

方法1:获取底层JDBC CallableStatement手动设置scale

这是最直接的实现方式,通过Hibernate的unwrap方法拿到原生JDBC的CallableStatement,手动注册输出参数并指定scale:

EntityManager em = ...; // 获取已初始化的EntityManager

StoredProcedureQuery query = em.createStoredProcedureQuery("test");
// 先按常规方式注册参数(无需指定scale)
query.registerStoredProcedureParameter("aDecimal", BigDecimal.class, ParameterMode.OUT);

// 解锁底层JDBC CallableStatement
CallableStatement cs = query.unwrap(CallableStatement.class);
// 手动注册输出参数并指定scale,可通过参数名称或索引(需对应存储过程中的参数位置)
cs.registerOutParameter("aDecimal", Types.DECIMAL, 34);

// 执行存储过程
query.execute();

// 获取输出参数结果
BigDecimal result = (BigDecimal) query.getOutputParameterValue("aDecimal");

方法2:自定义Hibernate类型(复用场景)

如果需要多次使用固定scale的DECIMAL参数,可以自定义Hibernate类型封装scale设置:

public class BigDecimal34ScaleType extends BigDecimalType {
    @Override
    public int getScale() {
        return 34;
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws SQLException {
        if (value == null) {
            st.setNull(index, Types.DECIMAL);
        } else {
            BigDecimal decimal = (BigDecimal) value;
            st.setBigDecimal(index, decimal.setScale(34, RoundingMode.HALF_UP));
        }
    }
}

之后通过Hibernate的ProcedureCall(从Session转换而来)指定自定义类型:

Session session = em.unwrap(Session.class);
ProcedureCall call = session.createStoredProcedureCall("test");
// 注册参数并绑定自定义类型
call.registerParameter("aDecimal", BigDecimal.class, ParameterMode.OUT)
        .setHibernateType(new BigDecimal34ScaleType());

// 执行并获取结果
call.execute();
BigDecimal result = (BigDecimal) call.getOutputParameterValue("aDecimal");
补充说明
  • 方法1无需额外代码,适合临时场景;方法2适合需要重复使用相同scale参数的业务场景。
  • 针对SQL Server的DECIMAL(38,34)类型,显式指定scale为34可避免Hibernate映射时出现精度丢失或类型转换异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 00:50:21