2008-10-28 9 views
1

Nous utilisons Hibernate Spring MVC avec le filtre OpenSessionInView. Voici un problème que nous courons dans (code pseudo)Problème de transaction Hibernate

transaction 1 
load object foo 
transaction 1 end 

update foo's properties (not calling session.save or session.update but only foo's setters) 

validate foo (using hibernate validator) 
if validation fails ? 
go back to edit screen 
transaction 2 (read only) 
load form backing objects from db 
transaction 2 end 
go to view 
else 
transaction 3 
session.update(foo) 
transaction 3 end 

le problème que nous avons est si la validation échoue foo est marqué « sale » à la séance de mise en veille prolongée (puisque nous utilisons OpenSessionInView nous n'avons une session tout au long de la requête http), lorsque nous chargeons les objets backing de formulaire (comme une liste de certaines entités utilisant une requête HQL), hibernate avant d'effectuer les vérifications de requêtes s'il y a des objets sales dans la session, il voit que foo est et le vide, lorsque la transaction 2 est validée, les mises à jour sont écrites dans la base de données. Le problème est que même s'il s'agit d'une transaction en lecture seule et même si foo n'a pas été mis à jour dans la transaction 2, hibernate ne sait pas quel objet a été mis à jour dans quelle transaction et ne vide pas les objets de cette transaction. Des suggestions? Quelqu'un a couru dans le même problème avant

Mise à jour: ce poste jette un peu plus de lumière sur le problème: http://brian.pontarelli.com/2007/04/03/hibernate-pitfalls-part-2/

Répondre

1

Vous pouvez exécuter un get sur foo pour le mettre dans la session de mise en veille prolongée, puis le remplacer par l'objet vous avez créé ailleurs. Mais pour que cela fonctionne, vous devez connaître tous les identifiants de vos objets afin que les identifiants semblent corrects pour Hibernation.

+0

le truc est dans la session, que voulez-vous dire par le remplacer? – talg

+0

appel get (foo.id), puis fusionner (foo) – Elie

1

Il existe plusieurs options ici. Premièrement, vous n'avez pas réellement besoin de la transaction 2 puisque la session est ouverte, vous pouvez simplement charger les objets de sauvegarde à partir de la base de données, évitant ainsi la vérification incorrecte de la session. L'autre option consiste à expulser foo de la session après sa récupération et à utiliser ensuite session.merge() pour le rattacher à vos modifications à stocker. Avec hibernate, il est important de comprendre ce qui se passe exactement sous les couvertures. À chaque limite de validation, il tentera de vider toutes les modifications apportées aux objets de la session en cours, que les modifications aient été faites ou non dans la transaction en cours ou dans n'importe quelle transaction. C'est ainsi que vous n'avez pas besoin d'appeler session.update() pour tout objet déjà dans la session.

Hope this helps

0

Qu'en est-il en utilisant Session.clear() et/ou Session.evict()?

0

Qu'en est-il de définir singleSession = false sur le filtre? Cela pourrait mettre vos opérations dans des sessions séparées afin que vous n'ayez pas à gérer les problèmes de cache de 1er niveau. Sinon, vous voudrez probablement détacher/attacher vos objets manuellement, comme le suggère l'utilisateur ci-dessus. Vous pouvez également changer le FlushMode sur votre session si vous ne voulez pas que les choses soient vidées automatiquement (FlushMode.MANUAL).

1

Il y a un problème de conception ici. Pensez-vous qu'un ORM est une abstraction transparente de votre banque de données, ou pensez-vous qu'il s'agit d'un ensemble de bibliothèques de manipulation de données? Je dirais qu'Hibernate est la première. Sa raison d'être est d'éliminer la distinction entre votre état d'objet en mémoire et votre état de base de données. Il fournit des mécanismes de bas niveau pour vous permettre de séparer les deux et de les traiter séparément, mais en faisant cela, vous supprimez une grande partie de la valeur d'Hibernate.

Donc très simplement - Hibernate = votre base de données. Si vous ne voulez pas que quelque chose persiste, ne changez pas vos objets persistants.

Validez vos données avant de mettre à jour les objets de votre domaine. Bien sûr, validé les objets de domaine, mais c'est une dernière ligne de défense. Si vous obtenez une erreur de validation sur un objet persistant, ne pas avaler l'exception.À moins que vous ne l'empêchiez, Hibernate fera ce qu'il faut, c'est-à-dire fermer la session à cet endroit.

0

Implémentez une couche de service, jetez un coup d'œil à l'annotation @Transactional du printemps et marquez vos méthodes comme @Transactional (readOnly = true), le cas échéant.

Votre mode de vidage est probablement défini sur auto, ce qui signifie que vous n'avez pas vraiment le contrôle du moment où un commit DB se produit.

Vous pouvez également définir votre mode de vidage sur manuel, et vos services/repos essaieront uniquement de synchroniser la base de données avec votre application lorsque vous leur demanderez de le faire.

Questions connexes