IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Hibernate Java Discussion :

Hibernate, problème d'update


Sujet :

Hibernate Java

  1. #1
    Membre averti
    Inscrit en
    Février 2007
    Messages
    27
    Détails du profil
    Informations forums :
    Inscription : Février 2007
    Messages : 27
    Par défaut Hibernate, problème d'update
    Bonjour,

    Je rencontre un problème très étrange et je m'arrache les cheveux dessus depuis quelques jours.

    Pour résumer, lorsque je met à jour un champ d'un objet et que je fait un session.merge, ce n'est parfois pas le bon objet qui est mis à jour, ou parfois il met bien à jour mon objet mais m'en met à jour un autre avec des valeurs d'une mise à jour précédente ...

    Plus en détails :
    J'ai une table document, chaque document a une colonne ordre qui spécifie l'ordre d'affichage.
    Dans mon interface, j'ai des boutons haut/bas, pour faire passer un document au dessus/en dessous de celui du dessus/dessous. (donc modifier l'ordre des documents dans la liste)

    Quand l'utilisateur clique sur le bouton haut par exemple, je récupère dans la liste de documents affichée le document concerné par le clic, et celui du haut, puis je swappe les valeurs de la colonne "order" des deux documents, et je les sauvegarde (session.merge)

    Le résultat est que parfois la mise à jour n'a pas lieu, et d'autres documents de la liste sont modifiés arbitrairement avec des valeurs d'updates précédentes.

    D'après les tests que j'ai fait, mon code modifie bien les objets comme il faut, c'est au moment de mettre à jour la base de données avec hibernate que ca déconne ...

    De plus, dans les logs il y a parfois 3 ou 4 requêtes updates alors que mon code n'en appelle que 2 ...

    Clic sur un bouton
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    int y=coord.getY();
    Document currentDocument=list.get(y);
     
    int toSwapY=command.equals("up")?y-1:y+1;
    Document toSwapDocument=list.get(toSwapY);
     
    DocumentHandler.swapOrder(currentDocument, toSwapDocument);
    refreshDocumentList();
    Méthode swapOrder de DocumentHandler :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    public static void swapOrder(Document doc1, Document doc2){
    	int tmpOrder=doc1.getOrder();
    	doc1.setOrder(doc2.getOrder());
    	doc2.setOrder(tmpOrder);
    	HibernateUtil.update(doc1);
    	HibernateUtil.update(doc2);
    }
    Méthode update de HibernateUtil :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    public static void update(Object object){
    	Session session = HibernateUtil.currentSession();
    	Transaction tx= session.beginTransaction();
    	session.merge(object);
    	tx.commit();
    	HibernateUtil.closeSession();
    }
    Les méthodes equals et hashcode sont redéfinies pour Document (basées sur la clé primaire)

    Si quelqu'un pense avoir une idée d'où pourrait venir le problème, ou une piste ... là, j'avoue ne pas comprendre du tout ...

    Au pire je ferai une mise à jour en SQL, même si c'est un peu crasseux.

  2. #2
    Membre émérite
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    548
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 548
    Par défaut
    Fais un update() au lieu d'un merge()

    Sinon ta méthode HibernateUtil.update() n'est pas terrible parce que tu ouvres une transaction à chaque fois. Ce serait plus propre de faire la modification des deux objets dans une même transaction, histoire que si la 2e foire, tu ne te retrouve pas avec des données incohérentes.

  3. #3
    Membre averti
    Inscrit en
    Février 2007
    Messages
    27
    Détails du profil
    Informations forums :
    Inscription : Février 2007
    Messages : 27
    Par défaut
    Effectivement tu as raison, dans ce cas mieux vaudrait faire les deux updates au sein de la meme transaction, je change ca de suite

    Remplacer merge() par update() "ammeiliore" un peu mon problème, ca part en vrille moins souvent, mais j'ai toujours des updates indésirables, au lieu que ce soit une fois sur 2 c'est une fois sur 10 ...

    En regardant la trace SQL, je remarque aussi qu'il fait parfois des select avant de faire un update, j'avoue que je pige pas trop pourquoi ... surtout que les objets viennent d'être récupéres de la base

    Voilà les requêtes SQL exécutées dans les différents cas de figure :

    Quand ça marche, en utilisant update()
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    select document0_.id as id2_0_, document0_.title as title2_0_, document0_."order" as order4_2_0_, document0_.language_id as language5_2_0_, document0_1_.catalogue_id as catalogue2_3_0_, document0_.type as type2_0_ from document document0_ left outer join volume document0_1_ on document0_.id=document0_1_.id where document0_.id=?
    update document set title=?, "order"=?, language_id=? where id=?
    select document0_.id as id2_0_, document0_.title as title2_0_, document0_."order" as order4_2_0_, document0_.language_id as language5_2_0_, document0_1_.catalogue_id as catalogue2_3_0_, document0_.type as type2_0_ from document document0_ left outer join volume document0_1_ on document0_.id=document0_1_.id where document0_.id=?
    update document set title=?, "order"=?, language_id=? where id=?
    Quand ca déconne, en utilisant update() (3 requêtes SQL générées pour 2 appel à update() ...)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    update document set title=?, "order"=?, language_id=? where id=?
    update document set title=?, "order"=?, language_id=? where id=?
    select document0_.id as id2_0_, document0_.title as title2_0_, document0_."order" as order4_2_0_, document0_.language_id as language5_2_0_, document0_1_.catalogue_id as catalogue2_3_0_, document0_.type as type2_0_ from document document0_ left outer join volume document0_1_ on document0_.id=document0_1_.id where document0_.id=?
    update document set title=?, "order"=?, language_id=? where id=?
    Quand ca marche, en utilisant merge()
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    select document0_.id as id2_0_, document0_.title as title2_0_, document0_."order" as order4_2_0_, document0_.language_id as language5_2_0_, document0_1_.catalogue_id as catalogue2_3_0_, document0_.type as type2_0_ from document document0_ left outer join volume document0_1_ on document0_.id=document0_1_.id where document0_.id=?
    select document0_.id as id2_0_, document0_.title as title2_0_, document0_."order" as order4_2_0_, document0_.language_id as language5_2_0_, document0_1_.catalogue_id as catalogue2_3_0_, document0_.type as type2_0_ from document document0_ left outer join volume document0_1_ on document0_.id=document0_1_.id where document0_.id=?
    update document set title=?, "order"=?, language_id=? where id=?
    update document set title=?, "order"=?, language_id=? where id=?

    Quand ca déconne, en utilisant merge() (pareil, 3 updates au lieu de 2 ...)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    update document set title=?, "order"=?, language_id=? where id=?
    update document set title=?, "order"=?, language_id=? where id=?
    update document set title=?, "order"=?, language_id=? where id=?
    J'espère que c'est juste moi qui sais pas l'utiliser, et que hibernate est pas truffé de bugs ...

  4. #4
    Membre émérite
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    548
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 548
    Par défaut
    Tu fais ton update() dans une session qui vient d'être ouverte, donc cette session ne connait pas l'objet document et fait un select.

    Je pense que ta classe document a un lien vers un autre document (parent/fils ?) dans ce cas c'est probable que Hibernate cascade le update au fils/parent. Ca dépend de la façon dont tu as configuré ça dans le mapping (cascade="...")

    La façon la plus simple de travailler avec Hibernate est la suivante

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    ouvrir une session
    ouvrir une transaction
    try {
      charger les objets
      afficher et éventuellement modifier les objets
      commit de la transaction
    } catch (Exception e) {
      rollback de la transaction
    } finally {
     fermer la session
    }
    Si tu procédes comme ça tu n'as aucun update() ni merge() à appeler, Hibernate sait tout seul quels objets ont été modifiés et au moment du commit fait les modifications en base nécessaires.

    Sinon je te conseille de lire et relire le chapitre 10 de la doc ! http://www.hibernate.org/hib_docs/v3...jectstate.html

  5. #5
    Membre averti
    Inscrit en
    Février 2007
    Messages
    27
    Détails du profil
    Informations forums :
    Inscription : Février 2007
    Messages : 27
    Par défaut
    Merci pour ta réponse !

    Effectivement dans certains cas Document a une classe fille (Volume) Mais je ne modifie qu'une propriété de Document.

    J'ai résolu mon problème avec un update en HQL, toute autre méthode ne menant apparemment à rien.

    Je rencontre énormément de problèmes avec hibernate, par exemple un session.saveOrUpdate(obj) qui me génère une exception hibernate d'ID dupliqués alors que session.contain(obj) me renvoie false ...
    Enfin ca n'a pas de rapport avec ma question initiale.

    En fait j'aimerai bien comprendre comment c'est possible qu'il mette à jour les mauvais objets, j'aime pas résoudre les problèmes avec des palliatifs comme j'ai fait.

  6. #6
    Membre émérite
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    548
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 548
    Par défaut
    La méthode contains() te dit si cette instance est associée à la session. Ton problème est qu'une autre instance est déjà associée à la session.
    Il ne peut pas y avoir deux instances avec la même ID associée à une session.

  7. #7
    Membre averti
    Inscrit en
    Février 2007
    Messages
    27
    Détails du profil
    Informations forums :
    Inscription : Février 2007
    Messages : 27
    Par défaut
    Si contains() compare les objets avec l'opérateur ==, je comprend que ca ne fonctionne pas.
    Par contre si ca utilise equals, ca devrait marcher vu que je l'ai redéfini pour comparer par rapport aux ID des objets.

    Qui se porte volontaire pour aller regarder le code de hibernate ?

    Si je peut pas utiliser contains() pour savoir si un objet avec un ID donné est associé à ma session, qu'est-ce que je peut utiliser d'autre ?

  8. #8
    Membre émérite
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    548
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 548
    Par défaut
    Quand on parle d'instances, le test est ==.
    Si tu veux absolument tester si un objet est associé à la session tu peux faire

    if (session.load(id) == object)

    Mais tout test problèmes viennent du fait que tu utilise (mal) des objets détachés, alors si ce n'est pas nécessaire, fais plutôt comme je conseillais : avoir une seule session pour charger et modifier les données

  9. #9
    Membre averti
    Inscrit en
    Février 2007
    Messages
    27
    Détails du profil
    Informations forums :
    Inscription : Février 2007
    Messages : 27
    Par défaut
    Dans mes programmes de test tout marche nickel, mais ton commentaire m'a mis la puce à l'oreille !

    Mon appli est une appli web, je stocke la session dans une variable ThreadLocal (le HibernateUtil copié collé du site de hibernate si je me souviens bien), et ca doit être ça le problème : à chaque fois que la servlet a fini de s'exécuter, la session hibernate est perdue, or j'en ai souvent besoin d'une page à l'autre ...
    Par exemple récupérer des objets affichés dans une liste de la page précédente pour les modifier.

    Ce qu'il me faudrait c'est donc une session hibernate liée à la session Http ...

    J'ai vu ça, je sais pas trop si ca correspond à ce que je veut faire, ni même si c'est la bonne méthode : http://www.hibernate.org/43.html

  10. #10
    Membre émérite
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    548
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 548
    Par défaut
    Oui, c'est l'approche recommandée pour une appli web

  11. #11
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    3 276
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 3 276
    Par défaut
    Oui, c'est l'approche recommandée pour une appli web
    Pas nécessairement.
    Autant faire simple si on peut et à priori le cas présent ne nécessite pas obligatoirement cette manière de faire. (à moins que j'ai raté quelque chose)

  12. #12
    Membre averti
    Inscrit en
    Février 2007
    Messages
    27
    Détails du profil
    Informations forums :
    Inscription : Février 2007
    Messages : 27
    Par défaut
    Bon ca a l'air de marcher, c'est cool

    La solution miracle c'était de stocker ma session hibernate dans la session HTTP, du coup elle reste ouverte tout le temps.
    Je sais pas si c'est propre ou pas, mais ca m'évite de m'arracher les cheveux.

    En fait si la session venait à se fermer, je vois pas comment garantir que l'application continues à fonctionner, notemment à cause du lazy-loading qui se déclenche parfois (les objets récupéres dans la base sont très souvent conservés après que la servlet ait fini de s'exécuter, et réutilisés lors de la requête suivante du client)

    En tout cas merci pour les réponses !

  13. #13
    Membre émérite
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    548
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 548
    Par défaut
    En fait ta solution va te poser encore plus de problèmes.
    D'abord tu garde ta session ouverte sur une longue période, donc si pendant ce temps un autre utilisateur modifie les données que tu avais chargées, toi tu ne vois pas ces changements.

    En plus tu as un nombre de connexions limité à la base de données. Cela limite aussi le nombre de sessions ouvertes simultannément. Si la session reste ouverte dans la session HTTP, ton nombre d'utilisateurs est très limité...

  14. #14
    Membre averti
    Inscrit en
    Février 2007
    Messages
    27
    Détails du profil
    Informations forums :
    Inscription : Février 2007
    Messages : 27
    Par défaut
    Pour le problème du nombre de connexions limitées, je déconnecte à chaque fois que la servlet a fini de s'exécuter (je sais pas par contre si hibernate se déconnecte vraiment de la bdd ou si il remet simplement la connexion dans le pool c3p0)

    Pour les données pas mises à jour, c'est bizarre, mais ça n'a pas l'air de poser de problèmes.
    Le seul cas où ça foire c'est quand deux utilisateurs regardent la même page, et que l'un supprime un objet, l'autre se prend alors une erreur si il essaye de supprimer l'objet en question. Pour les modifications, ça doit aussi pouvoir poser problème dans certains cas.

    Mais de toute façons hibernate ou pas, le seul moyen d'empêcher ça c'est d'ouvrir une transaction et de recharger tous les objets nécessaires au traitement avant chaque traitement, et d'annuler + mettre à jour l'interface si y a un problème.

    Le seul moyen que j'ai trouvé pour garantir qu'il aille bien piocher les objets dans la base au lieu d'aller les chercher dans le cache de niveau 2 c'est de faire une requête HQL.

    Enfin j'avoue que quand j'ai lu ta réponse je me suis pas mal interrogé, je débute hibernate et c'est encore assez flou pour moi.

    Penses-tu que je vais dans le mur avec ma méthode ?

    Mon site n'a pas encore été testé à grande échelle alors ...

  15. #15
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    3 276
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 3 276
    Par défaut
    Tu devrais faire simple comme te l'a proposé the-gtm.
    Ouvre une session, une transaction, fais tes traitements, ferme tout.
    Si tu as besoin de faire du lazy-loading dans tes jsp, utilise par exemple un filtre de servlet comme c'est expliqué sur le site d'Hibernate.
    Essaie surtout de faire au plus simple. Ne garde pas une Session Hibernate ouverte longtemps.
    A moins vraiment que tu aies besoin de plusieurs "écrans" pour une seule transaction, inutile de mettre ta Session Hibernate en session Http.
    Passe les ids de tes objets de tes jsp à tes classes metiers et sers toi de ces ids pour faire les traitements sur tes objets.

  16. #16
    Membre extrêmement actif Avatar de Mister Nono
    Homme Profil pro
    Ingénieur Mathématiques et Informatique
    Inscrit en
    Septembre 2002
    Messages
    2 242
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur Mathématiques et Informatique
    Secteur : Santé

    Informations forums :
    Inscription : Septembre 2002
    Messages : 2 242
    Par défaut
    [quote=the-gtm;2488149]Tu fais ton update() dans une session qui vient d'être ouverte, donc cette session ne connait pas l'objet document et fait un select.

    Je pense que ta classe document a un lien vers un autre document (parent/fils ?) dans ce cas c'est probable que Hibernate cascade le update au fils/parent. Ca dépend de la façon dont tu as configuré ça dans le mapping (cascade="...")

    La façon la plus simple de travailler avec Hibernate est la suivante

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    ouvrir une session
    ouvrir une transaction
    try {
      charger les objets
      afficher et éventuellement modifier les objets
      commit de la transaction
    } catch (Exception e) {
      rollback de la transaction
    } finally {
     fermer la session
    }

    C'est comme cela que je fais dans mes application web et cela fonctionne très bien.

    A+

  17. #17
    Membre averti
    Inscrit en
    Février 2007
    Messages
    27
    Détails du profil
    Informations forums :
    Inscription : Février 2007
    Messages : 27
    Par défaut
    Bon j'ai réussi à faire tourner mon appli avec une session qui se ferme à la fin de l'exécution de la servlet, j'espère que c'est plus propre comme ça.

    Merci à tous !

  18. #18
    Membre extrêmement actif Avatar de Mister Nono
    Homme Profil pro
    Ingénieur Mathématiques et Informatique
    Inscrit en
    Septembre 2002
    Messages
    2 242
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur Mathématiques et Informatique
    Secteur : Santé

    Informations forums :
    Inscription : Septembre 2002
    Messages : 2 242
    Par défaut
    Citation Envoyé par TiMiD Voir le message
    Bon j'ai réussi à faire tourner mon appli avec une session qui se ferme à la fin de l'exécution de la servlet, j'espère que c'est plus propre comme ça.

    Merci à tous !
    C'était préférable.

    Salut.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 13/01/2010, 12h22
  2. hibernate : problème avec update
    Par sel3aa dans le forum Hibernate
    Réponses: 3
    Dernier message: 17/02/2009, 10h02
  3. Problème d'update Mysql/Hibernate
    Par fid35 dans le forum Hibernate
    Réponses: 5
    Dernier message: 01/09/2008, 15h55
  4. Problème requete update
    Par krfa1 dans le forum Langage SQL
    Réponses: 2
    Dernier message: 29/03/2005, 08h47
  5. problème pour updater une BD
    Par yoda_style dans le forum ASP
    Réponses: 6
    Dernier message: 17/03/2005, 10h56

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo