ResultSet :: getBigDecimal为joda-money抛出了scale异常

时间:2014-05-11 21:53:39

标签: java mysql jdbc joda-money

尝试从MySQL数据库的BigDecimal读取创建Joda-Money Money对象会引发错误。

此代码:

PreparedStatement p_stmt = ...;
ResultSet results = ...;
Money amount = Money.of(CurrencyUnit.USD, results.getBigDecimal("amount"));

抛出这个:

java.lang.ArithmeticException: Scale of amount 1.0000 is greater than the scale of the currency USD
    at org.joda.money.Money.of(Money.java:74)
    at core.DB.getMoney(DB.java:4821)

我实际上通过添加舍入来解决错误

Money amount = Money.of(CurrencyUnit.USD, results.getBigDecimal("amount"), RoundingMode.HALF_UP);

但是,我对这个解决方案持怀疑态度。这是最好的吗?。

我正在使用DECIMAL(19,4)按照this SO answer将资金值存储在数据库中。老实说,有点让我感到困惑的是,为什么那里的回答者要求DB上有4位小数精度,但我倾向于相信高价值的答案,我认为他们知道他们在谈论什么,我会后悔不遵循他们的咨询。然而,Joda-Money不喜欢美国货币的4位小数精度。也许DECIMAL(19,4)是一个国际标准,他们需要4位小数精度?不确定..

使问题简明扼要:

  1. RoundingMode.HALF_UP是解决此错误的理想解决方案吗?
  2. 我应该将MySQL DB的精度从DECIMAL(19,4)更改为DECIMAL(19,2)吗?
  3. 有没有办法改变Joda-Money的精确度?如果是的话:我应该吗?

3 个答案:

答案 0 :(得分:0)

是的,不是一个好的解决方案。你代表的是金钱,因此你无法向上/向下舍入并且几分钱:)。

问题是CurrencyUnit.USD的位数为2,例如:1.45,来自DB的内容可能是1.45343,可能导致比例不同。试着用BigMoney而不是Money代表钱。

BigMoney amount = BigMoney .of(CurrencyUnit.USD, results.getBigDecimal("amount"));

答案 1 :(得分:0)

有同样的问题并通过添加舍入模式解决它:不必要:

    @Test(dataProvider = "moneyValueProvider")
    public void testMoneyFromFloat(String currency, Float value) {
        CurrencyUnit cu = CurrencyUnit.of(currency);
        //Double dVal = Double.valueOf(value.toString());
        BigDecimal bigDecimal = new BigDecimal(value.toString());
        Money money = Money.of(cu, bigDecimal, RoundingMode.UNNECESSARY);
        Assert.assertNotNull(money);
        Assert.assertEquals(bigDecimal.doubleValue(), money.getAmount().doubleValue());
    }

    @DataProvider
    public static Object[][] moneyValueProvider() {
        return new Object[][] {
                    {"GBP", 22.5f},
                    {"GBP", 57.8f},
                    {"JPY", 15000.0f}

    };

答案 2 :(得分:0)

Joda Money和BigDecimal为每种货币类型定义了一个“比例”。

规模为否。货币使用的小数位数。

因此,美元的小数位数为2(小数点后两位小数)。

如果您尝试从具有两个以上小数位的来源实例化一个USD货币的Money实例,那么您将得到缩放错误。 例如

20.99 is fine
20.991 throws an error.

其他货币允许使用不同的小数位数。

根据文档RoundMode.UNNECESSARY,如果实际上需要舍入,则实际上将引发异常。

例如:假设USD的小数位数为2,则小数位数应为2。

使用:

Money money = Money.of(CurrencyUnit.USD, value, RoundingMode.UNNECESSARY);

值:

1.20 - works fine
1.200 - works fine as whilst extra digit rounding isn't necessary.
1.201 - throws an exception as rounding is necessary

周围的环境和金钱总是一个问题。 我的方法是在db中存储正确的比例

例如:如果美元是DECIMAL(19,2)

使用RoundingMode.HALF_UP

进行乘法和除法操作时要小心。 在除法之前进行乘法运算将减少舍入问题。 如果您在进行四舍五入时进行算术运算,那应该没问题。