BigDecimal转BigInteger时触发ArithmeticException: Rounding necessary异常求助
大家好,我现在需要把从数据库获取的BigDecimal类型的ID转换为BigInteger,但一直触发ArithmeticException: Rounding necessary异常。数据库里的ID值是整数154,我尝试了两种转换方式都没解决问题,麻烦大家帮忙看看哪里出问题了,谢谢!
初始代码
IntegrationsUtils.logMessage(Level.INFO, "WT Refund | Processing Transaction: " + id); BigInteger bigIntegerId = id.setScale(0, RoundingMode.DOWN).toBigInteger(); WTRefund wtRefundTransaction = getDaoFactory().getWtRefundDao() .findById(id.toBigInteger());
更新后的代码(仍报错)
BigInteger bigIntegerId = id.setScale(0, RoundingMode.DOWN).toBigInteger(); WTRefund wtRefundTransaction = getDaoFactory().getWtRefundDao().findById(bigIntegerId);
异常栈信息
java.lang.ArithmeticException: Rounding necessary at java.base/java.math.BigDecimal.commonNeedIncrement(BigDecimal.java:4717) at java.base/java.math.BigDecimal.needIncrement(BigDecimal.java:4767) at java.base/java.math.BigDecimal.divideAndRound(BigDecimal.java:4681) at java.base/java.math.BigDecimal.setScale(BigDecimal.java:2940) at java.base/java.math.BigDecimal.toBigIntegerExact(BigDecimal.java:3557) at org.hibernate.type.descriptor.java.BigIntegerTypeDescriptor.wrap(BigIntegerTypeDescriptor.java:88) at org.hibernate.type.descriptor.java.BigIntegerTypeDescriptor.wrap(BigIntegerTypeDescriptor.java:19) at org.hibernate.type.descriptor.sql.DecimalTypeDescriptor$2.doExtract(DecimalTypeDescriptor.java:63) at org.hibernate.type.descriptor.sql.BasicExtractor.extract(BasicExtractor.java:47) at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:257) at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:253) at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:243) at org.hibernate.type.AbstractStandardBasicType.hydrate(AbstractStandardBasicType.java:329) at org.hibernate.persister.entity.AbstractEntityPersister.hydrate(AbstractEntityPersister.java:3214) at org.hibernate.persister.entity.Loadable.hydrate(Loadable.java:94) at org.hibernate.loader.plan.exec.process.internal.EntityReferenceInitializerImpl.loadFromResultSet(EntityReferenceInitializerImpl.java:342) at org.hibernate.loader.plan.exec.process.internal.EntityReferenceInitializerImpl.hydrateEntityState(EntityReferenceInitializerImpl.java:269) at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.readRow(AbstractRowReader.java:80) at org.hibernate.loader.plan.exec.internal.EntityLoadQueryDetails$EntityLoaderRowReader.readRow(EntityLoadQueryDetails.java:288) at org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl.extractRows(ResultSetProcessorImpl.java:157) at org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl.extractResults(ResultSetProcessorImpl.java:94) at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:105) at org.hibernate.loader.entity.plan.AbstractLoadPlanBasedEntityLoader.load(AbstractLoadPlanBasedEntityLoader.java:285) at org.hibernate.persister.entity.AbstractEntityPersister.doLoad(AbstractEntityPersister.java:4521) at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:4511) at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:571) at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:539) at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:208) at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:327) at org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:108) at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:74) at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:118) at org.hibernate.internal.SessionImpl.fireLoadNoChecks(SessionImpl.java:1231) at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1220) at org.hibernate.internal.SessionImpl.access$2100(SessionImpl.java:202) at org.hibernate.internal.SessionImpl$IdentifierLoadAccessImpl.doLoad(SessionImpl.java:2835) at org.hibernate.internal.SessionImpl$IdentifierLoadAccessImpl.lambda$load$1(SessionImpl.java:2812) at org.hibernate.internal.SessionImpl$IdentifierLoadAccessImpl.perform(SessionImpl.java:2768) at org.hibernate.internal.SessionImpl$IdentifierLoadAccessImpl.load(SessionImpl.java:2812) at org.hibernate.internal.SessionImpl.find(SessionImpl.java:3400) at org.hibernate.internal.SessionImpl.find(SessionImpl.java:3362) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:311) at jdk.proxy2/jdk.proxy2.$Proxy117.find(Unknown Source) at com.tlx.afx.integrations.commons.persistence.dao.BaseDAOPrimary.findById(BaseDAOPrimary.java:96) at com.tlx.afx.integrations.commons.persistence.dao.BaseDAOPrimary$$FastClassBySpringCGLIB$$486dd08b.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) at com.tlx.afx.integrations.commons.persistence.dao.WTRefundDao$$EnhancerBySpringCGLIB$$a3538153.findById(<generated>) at com.tlx.afx.integrations.inbound.accountingentries.wtrefund.WTRefundService.listenForWTRefundTransactions(WTRefundService.java:65) at com.tlx.afx.integrations.inbound.accountingentries.AccountingEntriesJobsTrigger.listenForIncomingTransaction15(AccountingEntriesJobsTrigger.java:418) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84) at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:95) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:842)
问题分析
从异常栈能看出来,异常并不是出在你自己写的转换代码里,而是Hibernate执行findById查询时,把数据库返回的Decimal类型字段转换为BigInteger时触发的——具体是BigIntegerTypeDescriptor.wrap方法调用了toBigIntegerExact(),这个方法要求BigDecimal必须是精确的整数(没有小数部分),否则就会抛出Rounding necessary异常。
你说数据库里的ID是154,但可能实际存储的是带有微小小数位的数值(比如154.0000000001这种肉眼看不到的精度误差),或者数据库字段定义是Decimal类型且带有小数位,导致Hibernate转换时认为这不是精确的整数。
解决方案
这里有几个可行的处理思路:
直接用BigDecimal作为DAO查询参数
既然数据库字段是Decimal类型,建议把WTRefundDao的findById方法参数类型改成BigDecimal,直接用从数据库拿到的id去查询,完全避免转换过程中的精度问题。强制截断小数部分后转换
如果必须用BigInteger查询,可以用这两种方式确保转换成功:// 方式1:直接调用toBigInteger(),自动截断小数部分,不会抛异常 BigInteger bigIntegerId = id.toBigInteger(); // 方式2:先去掉末尾的零再转字符串,彻底避免精度问题 BigInteger bigIntegerId = new BigInteger(id.stripTrailingZeros().toPlainString());检查实体类ID字段类型
确认WTRefund实体类中的ID字段类型和数据库字段匹配:如果数据库是Decimal类型,实体类应该用BigDecimal而非BigInteger,这样Hibernate加载实体时就不会触发错误的类型转换。
备注:内容来源于stack exchange,提问作者Wajih Haider

