Question

Je travaille sur une application de base de données qui est principalement en lecture seule, mais il existe une table qui enregistre les mouvements des utilisateurs dans l'application et qui comporte un grand nombre d'écritures. Pour quelques milliers d'écritures, nous voyons quelques exceptions dans le journal des erreurs, comme ceci:

[WARN][2009-07-30 11:09:20,083][org.hibernate.util.JDBCExceptionReporter] SQL Error: 1062, SQLState: 23000
[ERROR][2009-07-30 11:09:20,083][org.hibernate.util.JDBCExceptionReporter] Duplicate entry '17011' for key 1
[ERROR][2009-07-30 11:09:20,083][org.hibernate.event.def.AbstractFlushingEventListener] Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
  at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:94)
  at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
  at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)

La table en question a le schéma suivant:

CREATE TABLE IF NOT EXISTS `my_table` (
  `id` int(11) NOT NULL,
  `data1` int(11) NOT NULL,
  `data2` int(11) NOT NULL,
  `timestamp` datetime default NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;

Et le mappage Hibernate XML correspondant:

<hibernate-mapping>
  <class name="mycorp.MyClass" table="my_table">
    <id name="id" column="id" type="java.lang.Integer">
      <generator class="increment"/>
    </id>
    <property name="data1" column="data1" type="java.lang.Integer"/>
    <property name="data2" column="data2" type="java.lang.Integer"/>
    <property name="timestamp" column="timestamp" type="java.util.Date"/>
  </class>
</hibernate-mapping>

Il est possible, bien que peu probable, que plusieurs instances de notre application Web écrivent simultanément dans la base de données, car les numéros de version de notre contexte d'application Web permettent de publier de manière transparente de nouvelles versions des applications. Les clients avec l’ancienne version de l’application mise en cache dans leur navigateur Web accéderaient ainsi aux anciennes versions du serveur, que nous annulons de déployer au bout de quelques semaines.

Quoi qu'il en soit, je ne suis pas convaincu que ce soit le problème, mais je soupçonne qu'il existe un problème de synchronisation entre MySQL et Hibernate. Est-ce que changer mon générateur en séquence, seqhilo ou hilo aiderait? En outre, si vous pouvez fournir un exemple de configuration d’un tel générateur dans MySQL, ce serait très utile, car la plupart des ressources en ligne sont simplement copiées-collées à partir des exemples extrêmement minimalistes du manuel d’Hibernate.

Était-ce utile?

La solution

L’incrément est définitivement mauvais si vous avez plusieurs processus écrivant dans la même table - vous êtes obligé d’avoir des collisions.

Puisque nous parlons de MySQL, la chose la plus simple à utiliser serait identité . Dans votre mappage Hibernate:

<generator class="identity"/>

Dans votre script MySQL:

CREATE TABLE IF NOT EXISTS `my_table` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `data1` int(11) NOT NULL,
  `data2` int(11) NOT NULL,
  `timestamp` datetime default NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;

Pour modifier une table existante:

ALTER TABLE `my_table`
  CHANGE COLUMN `id` `id` int(11) NOT NULL AUTO_INCREMENT=$NEW_VALUE$;

où $ NEW_VALUE $ doit être remplacé par le prochain identifiant disponible afin que la séquence ne soit pas réinitialisée à 1.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top