Evite o problema com BigDecimal ao migrar para Java 1.4 para Java 1.5+
-
26-09-2019 - |
Pergunta
Recentemente, migrei um aplicativo Java 1.4 para um ambiente Java 6. Infelizmente, encontrei um problema com o BigDecimal
armazenamento em um banco de dados Oracle. Para resumir, quando tento armazenar um "7.65E+7"
Valor bigdecimal (76,500,000.00
) no banco de dados, o Oracle armazena na realidade o valor de 7,650,000.00
. Este defeito é devido à reescrição do BigDecimal
aula em Java 1.5 (ver aqui).
No meu código, o BigDecimal
foi criado de um double
Usando esse tipo de código:
BigDecimal myBD = new BigDecimal("" + someDoubleValue);
someObject.setAmount(myBD);
// Now let Hibernate persists my object in DB...
Em mais de 99% dos casos, tudo funciona bem. Exceto que, em poucos casos, ocorre o bug mencionado acima. E isso é bastante irritante.
Se eu alterar o código anterior para evitar o uso do construtor de string de BigDecimal
, então eu não encontro o bug Nos meus casos de uso:
BigDecimal myBD = new BigDecimal(someDoubleValue);
someObject.setAmount(myBD);
// Now let Hibernate persists my object in DB...
No entanto, como posso ter certeza de que esta solução é a maneira correta de lidar com o uso de BigDecimal
?
Então, minha pergunta é saber como eu tenho para gerenciar meu BigDecimal
valores para evitar esse problema:
- Não use o
new BigDecimal(String)
construtor e use diretamente onew BigDecimal(double)
? - Forçar o Oracle a usar
toPlainString()
ao invés detoString()
método ao lidar comBigDecimal
(e neste caso como fazer isso)? - Alguma outra solução?
Informações do ambiente:
- Java 1.6.0_14
- Hibernate 2.1.8 (sim, é uma versão bastante antiga)
- Oracle JDBC 9.0.2.0 e também testado com 10.2.0.3.0
- Oracle Database 10.2.0.3.0
Editar: Eu testei o mesmo código em erro, mas com o Oracle JDBC versão 10.2.0.4.0 e o bug fez não ocorrer! O valor armazenado foi de fato 76,500,000.00
... Sobre a Changelog, talvez esteja relacionado ao bug #4711863.
Solução
Com as versões modernas do Hibernate, você pode usar o userType para mapear qualquer classe para um campo de banco de dados. Basta fazer um userType personalizado e usá -lo para mapear o objeto BigDecimal para a coluna do banco de dados.
Ver http://i-proving.com/space/technologies/hibernate/user+Types+in+hibernate
Outras dicas
Confissão: Eu pessoalmente não uso o Hibernate, mas você poderia simplesmente criar um MyBigDecimal subclasse cujo método tostring () chama topLainstring ()?
Também não tenho muita certeza dos méritos de passar um dobro para o cosntrocutor de BigDecimal - um duplo é inerentemente impreciso, a menos que o número seja composto inteiramente de adições de poderes de 2 (com restrições de alcance). O ponto principal do BigDecimal é contornar essas restrições em dobro.