Bonjour,
Je suis actuellement en train de développer une application de comptabilité nécessitant des calculs exacts. Ayant rencontré les problèmes d'arrondi qu'on connait avec FLOAT et DOUBLE (qui m'avaient été suggérés par Hibernate), j'ai tenté l'utilisation de DECIMAL(10,2).
Quelle ne fut pas ma surprise de voir que non seulement les problèmes d'arrondis étaient encore présents, mais il semble qu'en plus certaines valeurs soient interdites ! Par exemple, j'arrive à insérer les valeurs de 1.11 à 7.77 mais pas 8.88.
Après passage sur
les forums MySQL, la réponse à mon problème semble contenue dans
cet article sur IEEE 754 et son interprétation par Java, MySQL et différents compilateurs C/C++.
J'en suis donc réduit pour le moment à mapper mes valeurs comme des entiers et à prier pour que les conversions entier/double (pour l'affichage et la saisie dans mon application) ne posent pas de problème d'arrondi.
Quelqu'un a-t-il une meilleure idée ?
Merci d'avance.
Hibernate version: 3.0.2 ga
Mapping documents: Hibernate Annotations 3.0.2 ga :
Code:
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "test_decimal")
public class TestDecimal
{
@Id
@Column(name = "dec_value", columnDefinition = "DECIMAL(10,2)")
double dec_value;
public double getDec_value()
{
return dec_value;
}
public void setDec_value(double dec_value)
{
this.dec_value = dec_value;
}
}
Code between sessionFactory.openSession() and session.close():Code:
for(int i = 0; i < 10; i++)
{
double value = 0.0d;
try
{
TestDecimal td = new TestDecimal();
value = (double) (i * 1.11d);
td.setDec_value(value);
session.save(td);
}
catch(Exception e)
{
System.err.println("Erreur à l'insertion pour " + value);
}
}
Full stack trace of any exception that occurs:Code:
2007-04-24 10:08:44,086 ERROR [main] org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:301) - Could not synchronize database state with session
org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:103)
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:91)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:249)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:139)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at HibernateUtil.commitTransaction(HibernateUtil.java:139)
at TestTestDecimal.testInsert(TestTestDecimal.java:25)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:99)
at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:81)
at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
at org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:75)
at org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:45)
at org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:71)
at org.junit.internal.runners.TestClassMethodsRunner.run(TestClassMethodsRunner.java:35)
at org.junit.internal.runners.TestClassRunner$1.runUnprotected(TestClassRunner.java:42)
at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
at org.junit.internal.runners.TestClassRunner.run(TestClassRunner.java:52)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:38)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: java.sql.BatchUpdateException: Data truncation: Data truncated for column 'dec_value' at row 1
at com.mysql.jdbc.ServerPreparedStatement.executeBatch(ServerPreparedStatement.java:657)
at org.apache.commons.dbcp.DelegatingStatement.executeBatch(DelegatingStatement.java:294)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:242)
... 29 more
Name and version of the database you are using: MySQL 5.0.37 Community
The generated SQL (show_sql=true): insert into test_decimal (dec_value) values (?)