2012-02-09 3 views
0

J'utilise une session de mise en veille prolongée par modèle de demande pour mon application Web. Ma transaction jdbc commence au début de chaque requête Web et est validée à la fin.hibernate plusieurs threads empêchent plusieurs sauvegarder(), JTA nécessaire?

// environnement non géré idiome

Session sess = factory.openSession(); 
Transaction tx = null; 
try { 
    tx = sess.beginTransaction(); 

    // do some work 
    ... 

    tx.commit(); 
} 
catch (RuntimeException e) { 
    if (tx != null) tx.rollback(); 
    throw e; // or display error message 
} 
finally { 
    sess.close(); 
} 

Je suis confronté au problème où je teste l'existence d'une entité (A) en fonction de plusieurs paramètres et de faire un insert que si elle n » t existe.

public synchronized myMethod(param1, param2) { 
    MyEntityA entity = MyEntityADAO.findEntity(param1, param2) 
    if (entity == null) { 
     entity = .../create entity 
     MyEntityADAO.save(entity); 
    } 
} 

le problème est que la synchronisation ne permet pas parce que l'appel à MyEntityADAO.save() ne pas écrire en fait à la base de données lorsque le thread en cours d'exécution sort de la méthode et libère le verrou, l'écriture à la base de données se produit après la transaction est engagée ce qui est généralement ce dont j'ai besoin pour mon application, sauf pour quelques scénarios. Le code ci-dessus provoque plusieurs enregistrements enregistrés avec les mêmes paramètres dans un environnement multithread.

J'ai essayé d'exécuter le code enregistrer dans sa nouvelle session et de transaction:

public synchronized myMethod(param1, param2) { 
    MyEntityA entity = MyEntityADAO.findEntity(param1, param2) 
    if (entity == null) { 
     entity = .../create entity 
     Session session = HibernateUtil.createSession(); 
     MyEntityADAO.save(entity); 
     Transaction t = session.beginTransaction(); 

    } 
} 

les causes ci-dessus problèmes avec 2 sessions ouvertes chargement de la même collection avec mise en veille prolongée dans certains cas.

Devrais-je inclure chaque appel DAO dans sa propre transaction et utiliser la propagation de transaction avec JTA? Y a-t-il un moyen d'éviter JTA? Est-il possible de valider une transaction associée à la session principale après l'appel de MyEntityADAO.save() et d'appeler beginTransaction sur la session principale juste après et d'avoir la transaction validée à la fin de la requête comme c'est le cas maintenant?

Répondre

0

La solution qui a fonctionné pour moi et mes exigences particulières d'application essayant d'éviter les transactions JTA et imbriquées:

Utilisation de ManagedSessionContext car org.hibe rnate.context.ThreadLocalSessionContext se fermera et créera une nouvelle session pour chaque transaction. Vous rencontrerez des problèmes avec des entités auxquelles des collections sont associées si vous chargez ces entités dans plusieurs sessions ouvertes (lorsque vous créez plusieurs transactions pour une requête).

  • J'ouvre une session de mise en veille prolongée et le lier au contexte au début de ma demande web
  • Toute méthode de couche de service qui a besoin de tester l'existence avant d'insérer est marquée Synchronized, la transaction globale est engagée avec le insérer déclaration et une nouvelle transaction est lancée
  • a la fin de la demande de la transaction liée à la session est commited

    myMethod synchronisée publique (param1, param2) {

    MyEntityA entity = MyEntityADAO.findEntity(param1, param2) 
    if (entity == null) { 
         entity = .../create entity 
    
         MyEntityADAO.save(entity); 
         HibernateUtil.getCurrentSession().getTransaction().commit(); 
         HibernateUtil.getCurrentSession().getTransaction().begin(); 
    
    
    } 
    

    }

Je sais que laid et ne fonctionnera pas pour tout le monde dans tous les scenerio, mais après avoir fait une recherche très intense sur la gestion des transactions, les niveaux d'isolement, de verrouillage, versioning qui est la seule solution que j'ai trouvé cela a fonctionné pour moi. Je n'utilise pas Spring, et je n'utilise pas de conteneur Java EE, en utilisant Tomcat 6.

0

La cohérence des données dans la base de données ne devrait pas être compromise en faisant seulement une partie d'un changement atomique dans sa propre transaction. Et bien qu'une certaine synchronisation puisse fonctionner sur votre environnement, si vous avez besoin de mettre votre application en cluster, ou si plusieurs applications accèdent à la base de données, cela ne résoudra pas le problème.

Ce que vous devez faire est de mettre une contrainte unique dans la base de données sur [param1 - param2]. Cela entraînera l'annulation de l'une des deux transactions s'il existe une condition de concurrence. Si vous choisissez d'isoler encore le code de vérification/insertion dans sa propre transaction (parce que ce n'est pas un problème si cela réussit et que la transaction externe échoue), je ne vois pas comment JTA serait un problème. Supposons que vous utilisiez des EJB ou Spring, mettez simplement cette méthode dans son propre EJB/bean, et marquez la méthode comme transactionnelle, avec la propagation REQUIRES_NEW.

Le code ressemblerait donc à ceci:

// some code 
Long id = myBean.checkIfExistOrCreate(param1, param2); // this methos call starts a new transaction 
// now we're sure that the entity exists. Load it in the current session. 
MyEntity e = em.find(MyEntity.class, id); 

Si vous ne pouvez pas synchroniser checkIfExistOrCreate, puis essayez de l'appeler, attraper une exception qu'il pourrait jeter, et essayer de nouveau l'appeler:

Long id = null; 
try { 
    id = myBean.checkIfExistOrCreate(param1, param2); 
} 
catch (Exception e) { // a well-defined exception would be better 
    // the transaction roled back: retry 
    id = myBean.checkIfExistOrCreate(param1, param2); 
} 
// now we're sure that the entity exists. Load it in the current session. 
MyEntity e = em.find(MyEntity.class, id); 
+0

merci de votre contribution. J'ai les contraintes mises en place mais je n'attrape pas les exceptions hibernate/jdbc dans ma couche dao/service. semble être une bonne idée. Je vous remercie. – user979051

+0

J'attrape l'exception ConstraintViolationException et j'essaie d'obtenir l'entité existante dans le bloc de capture et d'obtenir une null..il y a une étape intermédiaire quand la transaction est marquée comme si "terminée" dans la base de données sous-jacente (mysql) et les contraintes de table sont "mis à jour" mais la base de données ne renverra pas encore la nouvelle ligne de la table? – user979051

+0

J'ai essayé de vider la session à la place dans la méthode/bloc synchronisé après la sauvegarde() .. obtenir l'exception moins souvent mais n'a pas encore résolu le problème – user979051