128 votes

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

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

  • A a une relation de plusieurs à plusieurs avec B. (B a inverse="true" )
  • B a une relation de plusieurs à un avec C. (J'ai cascade réglé sur "save-update" )
  • C est une sorte de tableau de types/catégories.

En outre, 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 possède un ensemble d'objets B différents, et ces objets B font référence au même objet C.

Quand j'appelle session.saveOrUpdate(myAObject) j'ai une erreur d'hibernation qui dit : "a different object with the same identifier value was already associated with the session: C" . Je sais qu'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 ? Il semble que cette situation ne soit pas si rare.

Au cours de mes recherches sur ce problème, j'ai vu des personnes suggérer l'utilisation de session.merge() Mais lorsque je fais cela, tous les objets "conflictuels" sont insérés dans la base de données en tant qu'objets vides avec toutes les valeurs définies comme nulles. Il est clair que ce n'est pas ce que nous voulons.

[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.

122voto

jbx Points 4063

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

Ainsi, la session Hibernate, qui gère les entités, garde 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 se réfèrent à la même ligne se réfèrent en fait à la même instance d'objet de C. On peut aussi désactiver la mise en cascade pour cette variable membre. De cette façon, lorsque B est persisté, C ne l'est pas. Vous devrez cependant sauvegarder C manuellement et séparément. Si C est une table de type/catégorie, il est probablement logique qu'il en soit ainsi.

42voto

an0r23j Points 496

Réglez simplement la cascade sur MERGE, cela devrait faire l'affaire.

22voto

user3682254 Points 1

Vous n'avez qu'une seule chose à faire. Exécutez session_object.clear() puis enregistrez le nouvel objet. Cette opération permet d'effacer la session (comme son nom l'indique) et de supprimer l'objet en double incriminé de votre session.

20voto

Ice Blue Points 82

Je suis d'accord avec @Hemant Kumar, merci beaucoup. Selon sa solution, j'ai résolu mon problème.

Par exemple

@Test
public void testSavePerson() {
    try (Session session = sessionFactory.openSession()) {
        Transaction tx = session.beginTransaction();
        Person person1 = new Person();
        Person person2 = new Person();
        person1.setName("222");
        person2.setName("111");
        session.save(person1);
        session.save(person2);
        tx.commit();
    }
}

Personne.java

public class Person {
    private int id;
    private String name;

    @Id
    @Column(name = "id")
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Basic
    @Column(name = "name")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Ce code fait toujours des erreurs dans mon application : A different object with the same identifier value was already associated with the session Plus tard, j'ai découvert que j'avais oublié de autoincrémenter ma clé primaire !

Ma solution est d'ajouter ce code sur votre clé primaire :

@GeneratedValue(strategy = GenerationType.AUTO)

12voto

rex roy Points 141

Cela signifie que vous essayez d'enregistrer plusieurs lignes de votre tableau 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;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X