2010-05-29 7 views
3

J'ai affaire à une application Java héritée avec une base de code importante et assez compliquée. Il y a un objet assez standard « User » qui est stocké dans le HttpSession entre les demandes, de sorte que les servlets faire des trucs comme ça en tête:Entités Hibernate stockées en tant que valeurs d'attribut HttpSession

HttpSession session = request.getSession(true); 
User user = (User)session.getAttribute("User"); 

L'ancienne couche d'authentification utilisateur (que je ne vais pas décrire, suffire à dire, il n'a pas utilisé une base de données) est remplacé par du code mappé à la base de données avec Hibernate. Donc, 'Utilisateur' est maintenant une entité Hibernate. Ma compréhension des cycles de vie des objets Hibernate est un peu floue, mais il semble que stocker 'User' dans HttpSession devient un problème, car il sera récupéré dans une transaction différente lors de la prochaine requête. Quelle est la bonne chose à faire ici? Puis-je simplement utiliser la méthode update() de l'objet Hibernate Session pour rattacher l'instance User la prochaine fois? Est ce que j'ai besoin de?

Répondre

1

En supposant que vous créez une nouvelle session d'hibernation pour chaque cycle de demande-réponse, il est possible de fusionner un objet détaché dans la nouvelle session d'hibernation, mais j'éviterais complètement cette approche.

Essayez plutôt de stocker une clé sur la session HttpSession qui peut être utilisée pour rechercher un utilisateur via hibernate pour chaque requête entrante. Si vous vous inquiétez des conséquences sur les performances de la consultation de la base de données pour récupérer quelque chose qui peut être stocké dans HttpSession, ne craignez pas - vous pouvez toujours utiliser un framework de mise en cache pris en charge par hibernate pour réduire le nombre de visites. Une autre façon d'améliorer les performances serait d'utiliser un verrouillage optimiste.

Bien que je n'aie pas regardé les sources d'hibernation, je pense qu'Hibernate utilise le modèle "Identity Map". Il s'agit d'une carte qui utilise l'ID de l'entité en tant que clé dans la carte et l'objet entité associé en tant que valeur dans la carte. Chaque fois qu'une entité est extraite de la session d'hibernation, Hibernate regarde la carte d'identité de la session pour voir si elle est là. Si c'est le cas, l'entité sera renvoyée de la carte. Si ce n'est pas le cas, il récupèrera l'entité de la base de données et la mettra sur la carte, puis retournera l'entité. Cela signifie que les requêtes consécutives qui accèdent à un utilisateur donné avec la même clé (id, userId, etc.) pour une session d'hibernation donnée recevront une référence au même objet User, et donc chaque requête sera capable de "voir" les modifications apportées à l'objet Utilisateur par l'autre requête. Pour cette raison, il est absolument impératif de créer une nouvelle session d'hibernation pour chaque requête entrante, afin que les demandes simultanées pour un utilisateur donné n'aient pas à verrouiller leurs threads respectifs sur leur objet User commun. Différentes sessions d'hibernation auront chacune leur propre carte d'identité et ne renverront donc pas de références au même objet Utilisateur.En essayant de fusionner un objet Utilisateur du HttpSession dans votre session d'hibernation, vous essayez essentiellement de manipuler IdentityMap d'hibernate directement, en remplaçant tout hibernate "pense" devrait être là avec quelque chose d'autre, et cela peut naturellement causer des problèmes. Comme je l'ai dit, bien qu'il soit possible de rattacher un objet détaché à une session d'hibernation, je l'éviterais. Bonne chance avec l'approche que vous prenez.

Je recommande vivement de lire ce qui suit, dans votre cas en particulier les sections sur les longues conversations et des objets détachés:

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/transactions.html

0

Utilisez session.merge(..)

(De l » Session docs Les):

L'état d'une instance transitoire ou détaché peut également être persistante comme une nouvelle instance persistante en appelant la fusion().

juste être sûr de override hashCode() and equals() properly.

0

Cela dépend de si l'utilisateur change au cours de la session, ou si vous voulez juste l'identifiant, le nom, etc. pour une recherche rapide. Vous pouvez toujours fusionner() l'utilisateur à partir de la base de données pour l'avoir à nouveau dans la session.

Vous n'avez pas besoin de fusionner l'utilisateur à chaque fois, car c'est un hit de base de données dont vous n'avez pas toujours besoin. Mais avec la fusion, vous rattachez l'utilisateur à la session en cours.

+1

rafraîchissement sur une entité non gérée est une exception! Il doit fusionner. – Affe

+0

@Affe: Correct. Poste édité pour refléter ceci – Daniel

Questions connexes