web-dev-qa-db-fra.com

Erreur Hibernate: un objet différent avec le même identifiant a déjà été associé à la session

J'ai essentiellement des objets dans cette configuration (le modèle de données réel est un peu plus complexe):

  • A a plusieurs relations avec B. (B a inverse="true")
  • B a plusieurs relations avec C. (J'ai cascade réglé sur "save-update")
  • C est un type de table type/catégorie.

De plus, je devrais probablement mentionner que les clés primaires sont générées par la base de données lors de la sauvegarde.

Avec mes données, je rencontre parfois des problèmes où A a un ensemble d’objets B différents, et ces objets B font référence au même objet C.

Lorsque j'appelle session.saveOrUpdate(myAObject), j'obtiens une erreur d'hibernation en disant: "a different object with the same identifier value was already associated with the session: C". Je sais que hibernate ne peut pas insérer/mettre à jour/supprimer le même objet deux fois dans la même session, mais existe-t-il un moyen de contourner ce problème? Cela ne semble pas être inhabituel.

Au cours de mes recherches sur ce problème, j'ai vu des personnes suggérer l'utilisation de session.merge(), mais lorsque je le fais, tous les objets "en conflit" sont insérés dans la base de données sous forme d'objets vides avec toutes les valeurs définies à null. Clairement, ce n'est pas ce que nous voulons.

[Edit] Une autre chose que j'ai oublié de mentionner est que (pour des raisons architecturales indépendantes de ma volonté), chaque lecture ou écriture doit être effectuée dans une session séparée.

64
John

C'est probablement parce que les objets B ne font pas référence à la même instance d'objet Java C. Ils font référence à la même ligne de la base de données (c'est-à-dire la même clé primaire) mais ils en sont des copies différentes. 

Donc, ce qui se passe, c'est que la session Hibernate, qui gère les entités, garderait la trace de l'objet Java correspondant à la ligne avec la même clé primaire.

Une option serait de s'assurer que les entités des objets B qui font référence à la même ligne se réfèrent réellement à la même instance d'objet de C. Sinon, désactivez la cascade pour cette variable membre. De cette façon, lorsque B est persisté, C ne l’est pas. Cependant, vous devrez enregistrer C manuellement, séparément. Si C est une table type/catégorie, il est probablement logique de l’être de cette façon.

71
jbx

Il suffit de régler la cascade pour fusionner, cela devrait faire l'affaire.

18
4n0r23j

Vous n'avez qu'à faire une chose. Exécutez session_object.clear(), puis enregistrez le nouvel objet. Cela effacera la session (comme son nom l'indique) et supprimera l'objet dupliqué incriminé de votre session.

7
dsk

Transférez la tâche d’attribution de l’ID d’objet d’Hibernate à la base de données en utilisant:

<generator class="native"/>

Cela a résolu le problème pour moi.

5
user2845946

Un moyen de résoudre le problème ci-dessus consiste à remplacer le hashcode().
Rincer également la session d'hibernation avant et après l'enregistrement. 

getHibernateTemplate().flush();

Définir explicitement l’objet détaché sur null est également utile.

3
Waqar

Cela signifie que vous essayez de sauvegarder plusieurs lignes dans votre table avec la référence au même objet.

vérifiez la propriété id de votre classe d'entité.

@Id
private Integer id;

à

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(unique = true, nullable = false)
private Integer id;
3
rex roy

Recherchez l'attribut "Cascade" dans Hibernate et supprimez-le. Lorsque vous définissez "Cascade" disponible, il appelle d'autres opérations (enregistrer, mettre à jour et supprimer) sur une autre entité en relation avec les classes associées. Donc, la même valeur d'identités se produira. Cela a fonctionné avec moi.

2
Nguyen Vu Quang

Je viens de tomber sur ce message mais en code c #. Pas sûr que ce soit pertinent (exactement le même message d'erreur cependant).

Je déboguais le code avec des points d'arrêt et développais certaines collections via des membres privés alors que le débogueur était à un point d'arrêt. Après avoir réexécuté le code sans fouiller dans les structures, le message d'erreur a disparu. Il semble que le fait d’enquêter sur les collections privées chargées paresseux ait obligé NHibernate à charger des choses qui n’étaient pas censées être chargées à ce moment-là (parce qu’elles faisaient partie de membres privés).

Le code lui-même est encapsulé dans une transaction assez complexe pouvant mettre à jour un grand nombre d'enregistrements et de nombreuses dépendances dans le cadre de cette transaction (processus d'importation).

J'espère un indice pour quiconque se heurtera au problème.

2

Ajoutez l'annotation @ GeneratedValueau bean que vous insérez.

2
Hemant kumar

J'ai eu cette erreur quelques jours après et j'ai trop tardé à réparer cette erreur. 

 public boolean save(OrderHeader header) {
    Session session = sessionFactory.openSession();


    Transaction transaction = session.beginTransaction();

    try {
        session.save(header);

        for (OrderDetail detail : header.getDetails()) {
            session.save(detail);
        }

        transaction.commit();
        session.close();

        return true;
    } catch (HibernateException exception) {

        exception.printStackTrace();
        transaction.rollback();
        return false;
    }
}

Avant d’obtenir cette erreur, je n’avais pas mentionné le type de génération d’ID sur l’objet OrderDetil. sans générer l'id de Orderdetails, il conserve l'identifiant à 0 pour chaque objet OrderDetail. c'est ce que #jbx a expliqué. Oui c'est la meilleure réponse. ce un exemple comment ça se passe. 

1
Buddhi

Essayez de placer le code de votre requête avant . Cela résoudra mon problème. change ça:

query1 
query2 - get the error 
update

pour ça:

query2
query1
update
1
user3738027

si vous utilisez EntityRepository, utilisez saveAndFlush au lieu de save

0
Jayen Chondigara

Si on laisse un onglet d'expressions dans mon IDE _ open qui faisait un appel d'hibernation sur l'objet à l'origine de cette exception. J'essayais de supprimer ce même objet. J'ai également eu un point d'arrêt sur l'appel de suppression qui semble être nécessaire pour que cette erreur se produise. Faire simplement en sorte qu'un autre onglet d'expressions devienne l'onglet avant ou modifier le paramètre de sorte que l'idé ne s'arrête pas sur les points d'arrêt a résolu ce problème.

0
Aaron Byrnes

J'ai rencontré le problème parce que la génération de clé primaire est fausse, quand j'insère une ligne comme celle-ci:

public void addTerminal(String typeOfDevice,Map<Byte,Integer> map) {
        // TODO Auto-generated method stub
        try {
            Set<Byte> keySet = map.keySet();
            for (Byte byte1 : keySet) {
                Device device=new Device();
                device.setNumDevice(DeviceCount.map.get(byte1));
                device.setTimestamp(System.currentTimeMillis());
                device.setTypeDevice(byte1);
                this.getHibernateTemplate().save(device);
            }
            System.out.println("hah");
        }catch (Exception e) {
            // TODO: handle exception
            logger.warn("wrong");
            logger.warn(e.getStackTrace()+e.getMessage());
        }
}

Je change la classe de générateur d'identifiant en identité

<id name="id" type="int">
    <column name="id" />
    <generator class="identity"  />
 </id>
0
张云风

Dans mon cas, seul flush () n'a pas fonctionné. Je devais utiliser clear () après flush ().

public Object merge(final Object detachedInstance)
    {
        this.getHibernateTemplate().flush();
        this.getHibernateTemplate().clear();
        try
        {
            this.getHibernateTemplate().evict(detachedInstance);
        }
}
0
shubhranshu

vous ne définissez peut-être pas l'identificateur de l'objet avant d'appeler la requête de mise à jour.

0
Fawad Khaliq