web-dev-qa-db-fra.com

Problème de concurrence JPA "À la sortie du lot, il contenait toujours des instructions JDBC"

J'ai un problème de concurrence que j'ai essayé de résoudre avec une boucle while qui tente d'enregistrer une entité plusieurs fois jusqu'à ce qu'elle atteigne un nombre maximal de tentatives. Je voudrais éviter de dire s'il existe ou non d'autres moyens de résoudre ce problème. J'ai d'autres articles sur Stackoverflow à ce sujet. :) Bref: il y a une contrainte unique sur une colonne qui est dérivée et comprend une partie numérique qui continue à augmenter pour éviter les collisions. En boucle, je:

  1. sélectionnez max (some_value)
  2. incrémenter le résultat
  3. tenter d'enregistrer un nouvel objet avec ce nouveau résultat
  4. vider explicitement l'entité et, si cela échoue en raison de l'index unique, j'attrape une DataAccessException.

Tout cela semble fonctionner sauf lorsque la boucle revient à l'étape 1 et tente de sélectionner, j'obtiens:

17:20:46,111 INFO  [org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl] (http-localhost/127.0.0.1:8080-3) HHH000010: On release of batch it still contained JDBC statements
17:20:46,111 INFO  [my.Class] (http-localhost/127.0.0.1:8080-3) MESSAGE="Failed to save to database. Will retry (retry count now at: 9) Exception: could not execute statement; SQL [n/a]; constraint [SCHEMA_NAME.UNIQUE_CONSTRAINT_NAME]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement"

Et une nouvelle exception est interceptée. Il semble que le premier vidage qui provoque la violation de contrainte unique et lève le DataAccessException n'efface pas le lot du gestionnaire d'entités. Quelle est la manière appropriée de gérer cela? J'utilise Spring avec JPA et je n'ai pas d'accès direct au gestionnaire d'entités. Je suppose que je pourrais l'injecter si j'en ai besoin, mais c'est une solution douloureuse à ce problème.

13
Chris Williams

Vous ne pouvez pas le faire - une fois que vous avez vidé quelque chose et qu'il échoue et qu'une exception est levée, la transaction sera marquée comme annulation. Cela signifie que peu importe que vous interceptiez l'exception et poursuivez, vous vous retrouverez avec un retour en arrière. En fait, peu importe quelle exception a été levée - par défaut, le gestionnaire de transactions de Spring annulera toutes les non vérifiées exceptions. Vous pouvez le surmonter en définissant spécifiquement un noRollbackFor sur le @Transactional annotation (à condition que vous utilisiez des transactions de pilote d'annotation)

Modifier - cela ne vous aidera pas non plus en cas de violation de cette contrainte, car la transaction sera probablement marquée comme annulation au niveau de la base de données.

11
paranoidAndroid

J'ai trouvé cette question obtenant la même erreur. Dans mon cas, le problème a été causé par une combinaison étrange de déclencheur et de verrouillage de ligne (voir PSQLException et problème de verrouillage lorsque le déclencheur a été ajouté sur la table ). Cependant, il a fallu un certain temps pour découvrir que cette erreur n'est que la conséquence d'une erreur principale et non la cause. Lorsque Hibernate vide la session et qu'une violation de contrainte se produit, il reçoit une exception JDBC et dans le bloc finalement, il essaie d'appeler abortBatch. Lorsqu'une instruction reste en session, Hibernate émet cet avertissement, bien que l'erreur réelle doive être recherchée quelque part avant.

0
Tomáš Záluský