如何通过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
相关产品推荐
相关产品推荐

