ResultSet::getBigDecimal выдает исключение масштаба для joda-money
Попытка создать Joda-Money Money
объект из чтения BigDecimal из базы данных MySQL выдает ошибку.
Этот код:
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)
хранить денежные значения в БД согласно этому SO ответу. Честно говоря, меня как-то смутило, почему ответчик призвал к точности в 4 десятичных знака в БД, но я склонен доверять дорогостоящим ответам, и я предполагаю, что они знают, о чем они говорят, и что я буду сожалеть, что не последовал их совет. И все же Joda-Money не любит точность в 4 десятичных знака с валютой США. Может быть DECIMAL(19,4)
был международный стандарт, где им нужна точность до 4 десятичных знаков? Точно сказать не могу..
Чтобы вопросы были краткими:
- Является
RoundingMode.HALF_UP
идеальное решение, чтобы решить эту ошибку? - Должен ли я изменить точность на БД MySQL с
DECIMAL(19,4)
вDECIMAL(19,2)
? - Есть ли способ изменить точность в Joda-Money? Если да: я должен?
3 ответа
Joda Money и BigDecimal определяют "масштаб" для каждого типа валюты.
Шкала нет. десятичных знаков, которые использует валюта.
Таким образом, доллар США имеет шкалу два (два знака после запятой).
Если вы попытаетесь создать экземпляр Money для валюты USD из источника, который имеет более двух знаков после запятой, вы получите ошибку масштабирования. например
20.99 is fine
20.991 throws an error.
Другие валюты допускают различное количество десятичных знаков.
Согласно документации RoundMode.UNNECESSARY
на самом деле выдает исключение, если действительно требуется округление.
Например: при условии, что в долларах США используется шкала 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
Округление и деньги всегда проблема. Мой подход заключается в том, чтобы хранить правильный масштаб в БД
Например: если USD, то DECIMAL(19,2)
использование RoundingMode.HALF_UP
,
Будьте осторожны при умножении и делении. Выполнение умножения перед делением приведет к меньшим проблемам округления. Если вы округляете в то время, когда вы делаете арифметику, вы должны быть в порядке.
Да, не очень хорошее решение. Вы представляете деньги, поэтому вы не можете округлить вверх / вниз и потерять несколько центов:).
Проблема в том, что CurrencyUnit.USD имеет количество цифр до 2, например: 1.45, а то, что исходит от БД, - что-то вроде 1.45343, что может привести к разнице в масштабе. Попробуйте представлять деньги с BigMoney вместо денег.
BigMoney amount = BigMoney .of(CurrencyUnit.USD, results.getBigDecimal("amount"));
Возникла такая же проблема и решите ее, добавив режим округления Ненужно:
@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}
};