-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 11 posts ] 
Author Message
 Post subject: [Hibernate 3]Problème d'objet dupliqué dans la même sessi
PostPosted: Tue Sep 06, 2005 5:13 am 
Beginner
Beginner

Joined: Wed May 25, 2005 8:25 am
Posts: 30
Bonjour,

J'ai un petit soucis que je n'arrive pas à regler depuis qq jours. J'ai quelques pistes pour résoudre le problème mais celui-ci persiste.

Je dispose d'une table User dans laquelle on peut trouver 1 ou plusieurs Missions.

La table Mission est liée à une table Client via l'id du client.

Au niveau des classes, User possède un Set de Missions.
La classe Mission possède 1 attribut de type Client.

Lorsque je crée 1 mission associée à un client X, tout ce passe sans problème.

Maintenant j'essaye d'ajouter une 2eme mission pour le User, cette 2eme mission est associée au même client que la 1ere mission que j'ai crée.

Ma méthode est d'aller recherche, le User complet avec toutes les missions.

Ensuite je récupère les infos du formulaire de saisie et je crée un objet mission ainsi qu'un nouvel objet Client.

J'ajoute ensuite cette nouvelle mission au Set de Missions que j'ai récupérée de la base.

Ensuite j'essaye de sauvegarder tout le user, et la se pose le problème :

Code:
a different object with the same identifier value was already associated with the session


J'ai ciblé le problème, le fait est que la mission que j'ai récupéré en base est associée au client X.
Si l'utilisateur ajoute une nouvelle mission avec un client déja existant dans le set, alors l'erreur survient.

J'ai eu qq infos ici : http://www.hibernate.org/109.html

J'ai donc ajouté une méthode equals() sur les id du client mais j'ai toujours le même problème.

Une idée ??


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 06, 2005 6:06 am 
Pro
Pro

Joined: Fri Sep 02, 2005 4:21 am
Posts: 206
Location: Vienna
Bonjour,

Ta description est certes détaillée, mais il est tout de même bien difficile de la suivre - tu connais ton problème par coeur, nous pas.

Mon conseil (pressant): envoie les éléments essentiels de ton mapping, un squelette de tes classes et un morceau de code (un test JUnit ?) illustrant ton problème.

Comme cela il est plus facile de comprendre et reproduire ton problème - et t'aider est alors un plaisir :-).

Erik


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 06, 2005 8:08 am 
Beginner
Beginner

Joined: Wed May 25, 2005 8:25 am
Posts: 30
effectivement ma crainte était d'avoir du mal à être compris :) je vais essayer de rectifier le tir.

Tout d'abord le fichier de mapping hibernate :

Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0/EN"
         "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >

<hibernate-mapping default-access="property" default-lazy="false" auto-import="true">

   <class name="com.whoswho.model.LdapUser" table="LDAPUSER">
      <id name="id" column="ID" type="integer">
         <generator class="native"/>
      </id>
      <property name="uid">
         <column name="LABEL"/>
      </property>
      <set name="missions" table="USER_MISSION" cascade="save-update">
         <key column="USER_ID"/>
         <many-to-many class="com.whoswho.model.Mission" column="MISSION_ID"/>
      </set>
   </class>

   <class name="com.whoswho.model.Mission" table="MISSION">
      <id name="id" column="ID" type="integer">
         <generator class="native"/>
      </id>
      <property name="label">
         <column name="DESCRIPTION"/>
      </property>
      <property name="startDate" >
         <column name="START_DATE" sql-type="DATE"/>
      </property>
      <property name="endDate">
         <column name="END_DATE" sql-type="DATE"/>
      </property>
      
      <many-to-one name="client" column="CLIENT_ID" class="com.whoswho.model.Client" cascade="save-update"/>
      
   </class>

   <class name="com.whoswho.model.Client" table="CLIENT">
      <id name="id" column="ID" type="integer">
         <generator class="native"/>
      </id>
      <property name="label" >
         <column name="NAME"/>
      </property>
   </class>
</hibernate-mapping>


Les attributs des différentes classes (j'ai supprimé les getters et setters):
LdapUser :


Code:
public class LdapUser implements Comparable, Serializable {
   private Integer id;

   private String uid = null;

   private String name = null;

   private String email = null;

   private String mobile = null;

   private String title = null;

   private Set missions = null;

...


Mission :
Code:
public class Mission implements Serializable, Comparable{
   private Integer id;

   private Date startDate;

   private Date endDate;

   private Client client;

   private Set functions;

   private Set technologies;

   private String label;
...


Client :

Code:
public class Client implements Serializable
{
   private Integer id;

   private String label;

   public Client() {
   }


Voilà pour le code.

Maintenant une petit explication.
Imaginons que j'ai un LdapUser dans ma base, celui-ci ayant une mission TOTO associée à un client X.

Maintenant j'essaye d'ajouter une 2eme mission au LdapUser, cette mission possède le même client que la 1ere.

Ma démarche est la suivante : une fois le formulaire rempli et validé par le user, je récupère les données.

Les données sont : Nom de la mission, date début / date fin, ainsi que l'id du client sélectionné (une liste de tous les clients en base est présente dans le formulaire).

Une fois les données récupérées, je crée un nouvel objet mission en lui affectant les champs, je vais récupéré le client associé à l'id selectionné.

Ensuite je récupère la liste des missions pour le LdapUser, je récupère donc un set de missions (ici il n'y en a qu'une dans mon exemple).

JE fais ensuite un add de la nouvelle mission que j'ai crée dans le set que j'ai récupéré en base.

Ensuite j'essaye de stocker le LdapUser modifié.

Et la j'ai le problème. Le fait est que le LdapUser que je récupère de la base possède une mission avec le Client X et que j'ai un 2eme objet client X qui est identique mais qui n'est pas le même objet (vu que je viens de le créer avec ma nouvelle mission) et ca ne plait pas à hibernate.

Donc l'astuce que j'ai trouvée pour le moment, qui n'est pas très propre et de regarder si le client que j'essaye d'ajouter à ma nouvelle mission n'est pas déja présent dans le set de missions que j'ai récupéré de la base.

Si c'est le cas, je prends la référence du client du set récupéré que je place dans la nouvelle mission. Dans ce cas à la sauvegarde, hibernate ne trouve pas d'objet dupliqué.

J'espere avoir été un peu plus clair, je traine ce problème depuis 2j et je n'arrive pas à m'en sortir.

Si vous avez besoin de détails supplémentaires n'hesitez pas


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 06, 2005 8:43 am 
Regular
Regular

Joined: Sat May 15, 2004 4:27 am
Posts: 79
@partyboy

J'ai déja eu ce problème aussi. Et je crois d'ailleurs que c'était dans un
peu pres le même contexte. Pour m'en sortir j'utilisais la fonction evict
sur l'object qui pose problème. En faisant ça, je le vire de ma session :

session.evict(user);

Il est normal que cette erreur apparaisse pour Hibernate. L'identité de l'
objet, le premier, que du manipule n'est pas la même que le second que
tu instancies. Donc, il te suffit de charger le premier objet et le supprimer
par la suite :

User user = (User) session.load(User.class, userId);
session.evict(user);

A+

----
Sébastien

PS : please si ça t'aide augmente mon crédit stp...


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 06, 2005 10:17 am 
Beginner
Beginner

Joined: Wed May 25, 2005 8:25 am
Posts: 30
Ca n'a pas l'air de fonctionner :'(

je fais des evict un peu partout pour etre sur de virer tous les objets mais rien n'y fait.

je précise que j'utilise spring et que mes classes étendent HibernateDaoSupport et que j'utilise l'objet hibernateTemplate.

Merci


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 06, 2005 2:21 pm 
Pro
Pro

Joined: Fri Sep 02, 2005 4:21 am
Posts: 206
Location: Vienna
@partyboy

J'ai trouvé l'origine de ton problème. La difficulté est de savoir quelle solution convient dans ton environnement.

Tout d'abord: si tu effectues toutes les opérations que tu décris (récupérations des données + stockage) dans une seule et même transaction (ou Session) - tout se passe bien.

Si j'utilise deux transactions différentes (je pense que c'est ce que tu fais)
1) pour la recherche du client
2) pour la recherche du LdapUser + le stockage
alors j'ai aussi ton exception.

Une solution simple serait donc de tout faire dans une transaction - mais ton environnement étant certainement très différent de celui de mes tests, je ne sais si c'est possible.

Une alternative serait de "remettre" le client trouvé dans la seconde transaction. Pour cela il faut utiliser session.lock(ton objet Client, LockMode.READ).

J'espère que mes indications sont compréhensibles et utilisables.

Si ce n'était pas le cas, je peux poster mes tests (si je ne le fais pas maintenant, c'est que j'ai mon propre "framework" pour tester et que mon code n'est sans doute pas compréhensible sans explications supplémentaires - et que je suis un gros fainéant ;-), mais je crois - à la vue de ton diagnostic détaillé que tu vas trouver le reste du chemin toi-même.

Remarque: j'utilise aussi Spring.

Erik


Top
 Profile  
 
 Post subject:
PostPosted: Wed Sep 07, 2005 9:49 am 
Beginner
Beginner

Joined: Wed May 25, 2005 8:25 am
Posts: 30
@ErikFK

Merci pour tes recherches, désolé de ne pas avoir pu répondre avant mais on a eu un soucis internet aujourd'hui.

Je vais essayer ta méthode.

En attendant, j'ai trouvé une solution alternative, que je ne trouve pas trop mal tout comptes fait.

Lorsque j'essaye d'ajouter une mission avec un client, je boucle sur toutes les missions d'un user à la recherche d'un client identique.

Si je trouve un client identique, je prends alors la référence de celui-ci pour le mettre dans ma nouvelle mission. Etant le même objet en mémoire, hibernate ne me met plus l'erreur. De plus cela m'evite d'aller chercher une première fois le client pour l'ajouter à ma nouvelle mission, ca me fait 1 accès base en moins.


Mais bon je vais essayer ta méthode que je trouve plus élégante.

Je te tiens informé, j'espere avoir le temps de tester rapidement !


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 13, 2005 4:42 am 
Beginner
Beginner

Joined: Wed May 25, 2005 8:25 am
Posts: 30
Bonjour !

@ErikFK

J'ai essayé ta méthode, effectivement je n'utilise pas la même session.

Mai justement j'aimerai pouvoir utiliser la même session durant tout mon traitement.

Comme tu utilises spring tu pourras peut être m'aider.

Mes classes DAO étendent HibernateDaoSupport pour le support Hibernate.

J'utilise donc le getHibernateTemplate.

Apparemment il crée toujours une nouvelle session. Je peux lui dire de ne pas le faire en appelant la méthode : setAlwaysUseNewSession(false).

Le problème est que j'aimerais qu'hibernate utilise cette méthode par défaut.

Est-il possible de le setter qq part dans le fichier de config ??

Merci


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 13, 2005 5:20 am 
Pro
Pro

Joined: Fri Sep 02, 2005 4:21 am
Posts: 206
Location: Vienna
Bonjour !

partyboy wrote:
Apparemment il crée toujours une nouvelle session. Je peux lui dire de ne pas le faire en appelant la méthode : setAlwaysUseNewSession(false).
Le problème est que j'aimerais qu'hibernate utilise cette méthode par défaut.

Une remarque/rectification: l'utilisation d'une nouvelle Session à chaque appel est le fait de Spring - Hibernate n'a rien à voir la dedans.

Comme tous tes DAOs étendent HibernateDaoSupport tu pourrais ajouter
Code:
<property name="alwaysUseNewSession">
  <value>false</value>
</property>

à tous tes DAOs pour obtenir le même résultat.

Cela dit je déconseille (assez vivement) cette méthode. Utiliser toujours la même Session va te "tomber sur la tête" tôt tard - sauf si ton application ne fait pas grand chose, et cela pendant pas très longtemps ;-).

La méthode correcte, qui demande plus de réflexion, est de définir des transactions, avec point de départ et de terminaison. Tu dois te demander quelles sont les opérations qui forment un bloc tel que soit toutes tes données sont dans la DB, soit rien n'est écrit (bref pour garantir d'avoir des données consistantes dans la DB même s'il se produisait quelque malencontreuse exception...).

Même si c'est parfois (pas toujours) un travail difficile, c'est aussi un travail indispensable - avoir des données endommagées est sûrement beaucoup plus désagréable...

Erik


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 13, 2005 5:38 am 
Beginner
Beginner

Joined: Wed May 25, 2005 8:25 am
Posts: 30
Salut !

Je suis tout à fait d'accord avec toi en ce qui concerne les transactions, c'est d'ailleurs ce que je faisais avant.

Mais j'ai récupéré ce projet et je continue le développment, et je tombe sur pas mal de soucis que je n'avais pas auparavant, quand je faisais moi même les opérations ...

Je vais essayer de modifier mes traitements pour assembler mes accès pour 1 opération dans 1 méthode pour bien gérer les transactions.

Merci pour ton aide !


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 15, 2005 5:55 am 
Beginner
Beginner

Joined: Wed May 25, 2005 8:25 am
Posts: 30
Bonjour à tous,

je me permet de ressortir ce vieux topic pour eviter d'en recréer un étant donné que j'ai le même problème qui me revient sur un projet différent et j'aimerais le résoudre une fois pour toute.


Donc au niveau des technologies utilisées, j'utilise toujours hibernate 3 / spring et Tomcat en serveur.

Mes tables sont : Center, Cycle, Patient, Product, Status.

Product, Status sont de simples tables de références.


Voiçi les hbm des 2 tables qui nous interesse pour le problème (Center et Cycle) :

Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping
      default-lazy="false"
      auto-import="true"
      default-cascade="save-update"
      package="com.appli.model">
   
   <class name="Center" table="CENTER">
      <id name="id" type="integer">
         <generator class="sequence">
            <param name="sequence">center_id_sequence</param>
         </generator>
      </id>

      <property name="name" type="string" length="50" not-null="true"/>
      <property name="address" type="string" length="100" not-null="true"/>
      <property name="postalCode" type="string" length="50" not-null="true"/>
      <property name="area" type="string" length="50" not-null="true"/>
      
      <!-- Patient Association -->
      
      <set name="patients" inverse="true">
         <key column="center"/>
         <one-to-many class="Patient"/>
      </set>
      
   </class>

</hibernate-mapping>


La table Cycle :

Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping
      default-lazy="false"
      auto-import="true"
      default-cascade="save-update"
      package="com.appli.model">
   
   <class name="Cycle" table="CYCLE">
      <id name="id" type="integer">
         <generator class="sequence">
            <param name="sequence">cycle_id_sequence</param>
         </generator>
      </id>
      
      <property name="cycleNumber" type="integer" length="50" not-null="true"/>
      <property name="q300Iu" type="integer" length="50" not-null="true"/>
      <property name="q600Iu" type="integer" length="50" not-null="true"/>
      <property name="q900Iu" type="integer" length="50" not-null="true"/>
      <property name="startDate" type="date" not-null="true"/>
      <property name="createDate" type="date" not-null="true"/>
      <property name="modifiedDate" type="date" not-null="true"/>
      
      
      <!-- Patient Association -->
      
      <many-to-one name="patient" class="Patient" not-null="true"/>
      
      <!-- Center Association -->
      
      <many-to-one name="center" class="Center" not-null="true"/>
      
      <!-- Status Association -->
      
      <many-to-one name="status" class="Status" not-null="true"/>
      
      <!-- Product Association -->
      
      <many-to-one name="product" class="Product" not-null="true"/>
      
      
   </class>

</hibernate-mapping>


Mon application fonctionne comme comme suit :

Un utilisateur se connecte via un login de centre (Center :) ).

Si le user / mot de passe sont corrects je charge le center de la base et je le stocke dans ma session http.

Le user peut ensuite aller voir la liste des patients du centre. Un clic sur un patient lui permet de creer des cycles.

J'ai donc un formulaire qui permet d'entrer les infos du cycle.

Une fois le formulaire validé, je crée un objet Cycle et je lui stocke les infos. Au niveau du Center, je vais chercher celui que j'ai stocké dans ma session http et je le set dans mon objet Cycle.

Une fois l'objet Cycle rempli je fais un save et la j'obtient l'erreur :

Code:
org.springframework.orm.hibernate3.HibernateSystemException: a different object with the same identifier value was already associated with the session: [com.appli.model.Center#1]; nested exception is org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.appli.Center#1]


Voiçi ma classe ou je fais le save :

Code:
package com.appli.orm.hibernate.dao;

import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.appli.dao.CycleDao;
import com.appli.model.Center;
import com.appli.model.Cycle;
import com.appli.orm.hibernate.interceptor.CycleInterceptor;

public class CycleDaoImpl extends HibernateDaoSupport implements CycleDao{

   public void save(Cycle cycle) {
      
      getHibernateTemplate().setEntityInterceptor(new CycleInterceptor());
      getHibernateTemplate().saveOrUpdate(cycle);   
   }
}


J'ai essayé de faire un evict de l'objet cycle que j'ai en paramètre mais ca ne change rien.

J'ai également configuré spring pour que l'hibernateTemplate crée une nouvelle session à chaque fois.

Effectivement lorsque je fais un débug de mon appli et que je met un point d'arret avant de sauvegarder, dans ma session hibernate j'ai bien un objet de type Center mais je n'ai pas moyen de l'enlever ...

J'ai essayé un flush également mais rien n'y fait.

Ce post est mon dernier recours avant de faire moi même la sauvegarde avec une requete HQL, mais bon j'aimerai eviter d'utiliser cette méthode.

En esperant que vous trouverez une solution :)

Merci


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 11 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.