Erreur Hibernate: org.hibernate.NonUniqueObjectException: un objet différent avec la même valeur d'identifiant était déjà associé à la session

114

J'ai deux objets utilisateur et pendant que j'essaye de sauvegarder l'objet en utilisant

session.save(userObj);

Je reçois l'erreur suivante:

Caused by: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:
[com.pojo.rtrequests.User#com.pojo.rtrequests.User@d079b40b]

Je crée la session en utilisant

BaseHibernateDAO dao = new BaseHibernateDAO();          

rtsession = dao.getSession(userData.getRegion(),
                           BaseHibernateDAO.RTREQUESTS_DATABASE_NAME);

rttrans = rtsession.beginTransaction();
rttrans.begin();

rtsession.save(userObj1);
rtsession.save(userObj2);

rtsession.flush();
rttrans.commit();

rtsession.close(); // in finally block

J'ai aussi essayé de faire la session.clear()sauvegarde avant, toujours pas de chance.

C'est pour la première fois que j'obtiens l'objet de session lorsqu'une demande d'utilisateur arrive, donc je comprends pourquoi je dis que cet objet est présent dans la session.

Aucune suggestion?

dur
la source
Voici un autre fil merveilleux qui a aidé à résoudre mon problème getj2ee.over-blog.com
...

Réponses:

173

J'ai eu cette erreur plusieurs fois et cela peut être assez difficile à retrouver ...

En gros, ce que dit hibernate, c'est que vous avez deux objets qui ont le même identifiant (même clé primaire) mais qui ne sont pas le même objet.

Je vous suggère de décomposer votre code, c'est-à-dire de commenter les bits jusqu'à ce que l'erreur disparaisse, puis de remettre le code jusqu'à ce qu'il revienne et que vous devriez trouver l'erreur.

Cela se produit le plus souvent via des sauvegardes en cascade où il y a une sauvegarde en cascade entre les objets A et B, mais l'objet B a déjà été associé à la session mais n'est pas sur la même instance de B que celle sur A.

Quel générateur de clé primaire utilisez-vous?

La raison pour laquelle je pose la question est que cette erreur est liée à la façon dont vous dites à hibernate de vérifier l'état persistant d'un objet (c'est-à-dire si un objet est persistant ou non). L'erreur peut se produire parce que la mise en veille prolongée tente de conserver un objet qui est déjà persistant. En fait, si vous utilisez save hibernate, vous essaierez de conserver cet objet, et peut-être qu'il y a déjà un objet avec cette même clé primaire associée à la session.

Exemple

En supposant que vous ayez un objet de classe de mise en veille prolongée pour une table de 10 lignes basée sur une combinaison de clés primaires (colonne 1 et colonne 2). Maintenant, vous avez supprimé 5 lignes du tableau à un moment donné. Maintenant, si vous essayez à nouveau d'ajouter les mêmes 10 lignes, tandis que Hibernate tente de conserver les objets dans la base de données, 5 lignes qui ont déjà été supprimées seront ajoutées sans erreur. Maintenant, les 5 lignes restantes qui existent déjà, lèveront cette exception.

Donc, l'approche simple serait de vérifier si vous avez mis à jour / supprimé une valeur dans une table qui fait partie de quelque chose et que vous essayez plus tard d'insérer à nouveau les mêmes objets

Michael Wiles
la source
4
Belle réponse². La clé primaire était mon problème, résolu avec le GeneratedValue définissant une séquence pour postgresql.
Rodrigo Ferrari
2
J'ai eu le même problème. Dans mon cas, j'ai eu un objet recherché dans un code et j'ai essayé de construire un nouvel objet avec le même ID dans un autre morceau de code alors que le premier objet était encore à la session d'hibernation.
dellasavia
18

Ce n'est qu'un point où la mise en veille prolongée pose plus de problèmes qu'elle n'en résout. Dans mon cas, il y a beaucoup d'objets avec le même identifiant 0, car ils sont nouveaux et n'en ont pas. La base de données les génère. Quelque part, j'ai lu que 0 signaux Id non défini. La façon intuitive de les conserver est de les parcourir et de dire hibernate pour enregistrer les objets. Mais vous ne pouvez pas faire ça - "Bien sûr, vous devriez savoir que la mise en veille prolongée fonctionne de cette façon, donc vous devez ..." Alors maintenant je peux essayer de changer les identifiants en Long au lieu de long et voir si cela fonctionne. En fin de compte, il est plus facile de le faire avec un simple mappeur, car la mise en veille prolongée n'est qu'un fardeau supplémentaire intransparent. Un autre exemple: essayer de lire les paramètres d'une base de données et de les conserver dans une autre vous oblige à faire presque tout le travail manuellement.

Hein
la source
13
Je déteste aussi l'hibernation ... et les bases de données ... Après tous les problèmes qu'ils m'ont causés, je suppose qu'il est plus facile d'utiliser un fichier texte (je plaisante, mais quand même ...).
Igor Popov
Dans mon cas, il y a beaucoup d'objets avec le même identifiant 0, car ils sont nouveaux et n'en ont pas. La base de données les génère. Quelque part, j'ai lu que 0 signaux Id non défini. La façon intuitive de les conserver est de les parcourir et de dire hibernate pour enregistrer les objets . J'ai besoin de faire exactement cela. Pourriez-vous me dire quelle est la «méthode de mise en veille prolongée» pour faire cela?
Ramses
Dans mon cas, j'ai dû utiliser session.merge (myobject) car j'avais deux instances de cet objet. Habituellement, cela se produit lorsqu'une entité est persistante et détachée de la session. Une autre instance de cette entité est demandée pour la mise en veille prolongée. Cette seconde instance est restée attachée à la session. La première instance est modifiée. Plus d'informations sur getj2ee.over-blog.com/…
Reddymails
ce n'est pas la mise en veille prolongée qui crée des problèmes, mais le manque de compréhension de son fonctionnement
ACV
17

UTILISATION session.evict(object);La fonction de evict()méthode est utilisée pour supprimer une instance du cache de session. Donc, pour la première fois que vous enregistrez l'objet, enregistrez l'objet en appelant la session.save(object)méthode avant d'expulser l'objet du cache. De la même manière, mettez à jour l'objet en appelant session.saveOrUpdate(object)ou session.update(object)avant d'appeler evict ().

Rahul N
la source
11

Cela peut se produire lorsque vous avez utilisé le même objet de session pour la lecture et l'écriture. Comment? Supposons que vous ayez créé une session. Vous avez lu un enregistrement de la table des employés avec la clé primaire Emp_id = 101 Vous avez maintenant modifié l'enregistrement en Java. Et vous allez enregistrer l'enregistrement de l'employé dans la base de données. nous n'avons fermé nulle part ici. Comme l'objet qui a été lu, persiste également dans la session. Il entre en conflit avec l'objet que nous souhaitons écrire. D'où cette erreur.

Prashant Bari
la source
9

Comme quelqu'un l'a déjà souligné ci-dessus, j'ai rencontré ce problème lorsque j'avais cascade=allaux deux extrémités d'une one-to-manyrelation, supposons donc A -> B (un-à-plusieurs de A et plusieurs-à-un de B) et mettait à jour l'instance de B dans A, puis en appelant saveOrUpdate (A), cela entraînait une demande de sauvegarde circulaire, c'est-à-dire que la sauvegarde de A déclenche la sauvegarde de B qui déclenche la sauvegarde de A ... et dans la troisième instance, l'entité (de A) a été tentée de être ajouté à sessionPersistenceContext, l'exception duplicateObject a été levée.
  Je pourrais le résoudre en supprimant la cascade d'une extrémité.

Redzedi
la source
J'ai résolu mon problème avec stackoverflow.com/questions/4334970/…
Magno C
5

Vous pouvez utiliser session.merge(obj), si vous effectuez une sauvegarde avec différentes sessions avec le même objet persistant identifiant.
Cela a fonctionné, j'avais le même problème avant.

Pavan Kumar
la source
4

J'ai également rencontré ce problème et j'ai eu du mal à trouver l'erreur.

Le problème que j'ai eu était le suivant:

L'objet a été lu par un Dao avec une session d'hibernation différente.

Pour éviter cette exception, relisez simplement l'objet avec le dao qui va sauvegarder / mettre à jour cet objet plus tard.

alors:

class A{      

 readFoo(){
       someDaoA.read(myBadAssObject); //Different Session than in class B
    }

}

class B{



 saveFoo(){
       someDaoB.read(myBadAssObjectAgain); //Different Session than in class A
       [...]
       myBadAssObjectAgain.fooValue = 'bar';
       persist();
    }

}

J'espère que cela fera gagner beaucoup de temps à certaines personnes!

Frithjof
la source
4

J'ai rencontré ce problème par:

  1. Supprimer un objet (en utilisant HQL)
  2. Stocker immédiatement un nouvel objet avec le même identifiant

Je l'ai résolu en vidant les résultats après la suppression et en effaçant le cache avant d'enregistrer le nouvel objet

String delQuery = "DELETE FROM OasisNode";
session.createQuery( delQuery ).executeUpdate();
session.flush();
session.clear();
wbdarby
la source
4

Ce problème se produit lorsque nous mettons à jour le même objet de session, que nous avons utilisé pour récupérer l'objet de la base de données.

Vous pouvez utiliser la méthode de fusion de la mise en veille prolongée au lieu de la méthode de mise à jour.

Par exemple, utilisez d'abord session.get () et ensuite vous pouvez utiliser session.merge (object). Cette méthode ne créera aucun problème. Nous pouvons également utiliser la méthode merge () pour mettre à jour l'objet dans la base de données.

Jayendra Birtharia
la source
3

Récupérez l'objet dans la session, voici un exemple:

MyObject ob = null;
ob = (MyObject) session.get(MyObject.class, id);
chaussette_osg
la source
3
Bien essayé, mais l'objet "ob" est renvoyé sans les données mises à jour (étant donné qu'il a été mis à jour via l'application pendant l'exécution, entre les objets récupérés de la base de données et son enregistrement).
Alex
2

Vos mappages d'identifiants sont-ils corrects? Si la base de données est responsable de la création de l'ID via un identifiant, vous devez mapper votre objet utilisateur à celui-ci.

cwap
la source
2

J'ai rencontré ce problème avec la suppression d'un objet, ni expulser ni effacer aidé.

/**
 * Deletes the given entity, even if hibernate has an old reference to it.
 * If the entity has already disappeared due to a db cascade then noop.
 */
public void delete(final Object entity) {
  Object merged = null;
  try {
    merged = getSession().merge(entity);
  }
  catch (ObjectNotFoundException e) {
    // disappeared already due to cascade
    return;
  }
  getSession().delete(merged);
}
TimP
la source
2

avant la position où commencent les objets répétitifs, vous devez fermer la session, puis démarrer une nouvelle session

session.close();      
session = HibernateUtil.getSessionFactory().openSession();

ainsi, dans une session, il n'y a pas plus d'une entité qui a le même identifiant.

engtuncay
la source
2

En retard à la fête, mais peut aider pour les utilisateurs à venir -

J'ai eu ce problème lorsque je sélectionne un enregistrement à l'aide getsession() et que je mets à nouveau à jour un autre enregistrement avec le même identifiant en utilisant la même session provoque le problème. Code ajouté ci-dessous.

Customer existingCustomer=getSession().get(Customer.class,1);
Customer customerFromUi;// This customer details comiong from UI with identifer 1

getSession().update(customerFromUi);// Here the issue comes

Cela ne devrait jamais être fait. La solution consiste soit à expulser la session avant la mise à jour, soit à modifier la logique métier.

vipin cp
la source
1

Vérifiez si vous avez oublié de mettre @GenerateValue pour la colonne @Id. J'ai eu le même problème avec la relation plusieurs à plusieurs entre le film et le genre. Le programme a lancé une erreur Hibernate: org.hibernate.NonUniqueObjectException: un objet différent avec la même valeur d'identifiant était déjà associé à l'erreur de session. J'ai découvert plus tard que je dois juste m'assurer que vous avez @GenerateValue à la méthode get de GenreId.

Thupten
la source
comment puis-je appliquer votre solution à ma question? créer une colonne id dans la classe Task? stackoverflow.com/questions/55732955/…
sbattou
1

il suffit de vérifier l'identifiant s'il prend nul ou 0 comme

if(offersubformtwo.getId()!=null && offersubformtwo.getId()!=0)

dans ajouter ou mettre à jour où le contenu est défini du formulaire à Pojo

Ayan Sarkar
la source
1

Je suis nouveau sur NHibernate, et mon problème était que j'ai utilisé une session différente pour interroger mon objet que pour le sauvegarder. La session de sauvegarde ne connaissait donc pas l'objet.

Cela semble évident, mais à la lecture des réponses précédentes, je cherchais partout 2 objets, pas 2 sessions.

DustinA
la source
1

@GeneratedValue (strategy = GenerationType.IDENTITY), l'ajout de cette annotation à la propriété de clé primaire dans votre bean entité devrait résoudre ce problème.

Geai
la source
1

J'ai résolu ce problème.
En fait, cela se produit parce que nous avons oublié l'implémentation de la propriété Generator Type of PK dans la classe bean. Alors faites-en n'importe quel type comme

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;

quand on persiste les objets du bean, chaque objet acquiert le même identifiant, donc le premier objet est sauvegardé, quand un autre objet à persister alors HIB FW à travers ce type d' Exception: org.hibernate.NonUniqueObjectException:objet différent avec la même valeur d'identifiant était déjà associé à la session.

Ankit Sharma
la source
1

Le problème se produit car dans la même session de mise en veille prolongée, vous essayez de sauvegarder deux objets avec le même identifiant.Il y a deux solutions: -

  1. Cela se produit car vous n'avez pas configuré correctement votre fichier mapping.xml pour les champs id comme ci-dessous: -

    <id name="id">
      <column name="id" sql-type="bigint" not-null="true"/>
      <generator class="hibernateGeneratorClass"</generator>
    </id>
  2. Surchargez la méthode getsession pour accepter un paramètre comme isSessionClear, et effacez la session avant de retourner la session en cours comme ci-dessous

    public static Session getSession(boolean isSessionClear) {
        if (session.isOpen() && isSessionClear) {
            session.clear();
            return session;
        } else if (session.isOpen()) {
            return session;
        } else {
            return sessionFactory.openSession();
        }
    }

Cela entraînera l'effacement des objets de session existants et même si hibernate ne génère pas d'identifiant unique, en supposant que vous ayez correctement configuré votre base de données pour une clé primaire en utilisant quelque chose comme Auto_Increment, cela devrait fonctionner pour vous.

Rahul Kumar
la source
1

J'avais un problème similaire. Dans mon cas, j'avais oublié de définir la increment_byvaleur dans la base de données pour qu'elle soit la même que celle utilisée par cache_sizeetallocationSize . (Les flèches pointent vers les attributs mentionnés)

SQL:

CREATED         26.07.16
LAST_DDL_TIME   26.07.16
SEQUENCE_OWNER  MY
SEQUENCE_NAME   MY_ID_SEQ
MIN_VALUE       1
MAX_VALUE       9999999999999999999999999999
INCREMENT_BY    20 <-
CYCLE_FLAG      N
ORDER_FLAG      N
CACHE_SIZE      20 <-
LAST_NUMBER     180

Java:

@SequenceGenerator(name = "mySG", schema = "my", 
sequenceName = "my_id_seq", allocationSize = 20 <-)
kaba713
la source
1

A part ce que dit wbdarby , cela peut même arriver lorsqu'un objet est récupéré en donnant l'identifiant de l'objet à un HQL. Dans le cas où vous essayez de modifier les champs de l'objet et de le sauvegarder dans la base de données (la modification peut être une insertion, une suppression ou une mise à jour) au cours de la même session , cette erreur apparaîtra. Essayez d'effacer la session de mise en veille prolongée avant d'enregistrer votre objet modifié ou de créer une nouvelle session.

J'espère avoir aidé ;-)

Arash moradabadi
la source
1

J'ai la même erreur que je remplaçais mon ensemble par un nouveau obtenu de Jackson.

Pour résoudre ce problème, je garde l'ensemble existant, je supprime de l'ancien ensemble l'élément inconnu dans la nouvelle liste avec retainAll. Ensuite, j'ajoute les nouveaux avec addAll.

    this.oldSet.retainAll(newSet);
    this.oldSet.addAll(newSet);

Pas besoin d'avoir la Session et de la manipuler.

Ôrel
la source
1

Essaye ça. Le ci-dessous a fonctionné pour moi!

Dans le hbm.xmldossier

  1. Nous devons définir l' dynamic-updateattribut de la balise de classe sur true:

    <class dynamic-update="true">
  2. Définissez l'attribut de classe de la balise du générateur sous une colonne unique sur identity:

    <generator class="identity">

Remarque: définissez la colonne unique sur identityplutôt que assigned.

RaGa__M
la source
0

Une autre chose qui a fonctionné pour moi a été de rendre la variable d'instance Long au lieu de long


J'avais mon id long de variable de clé primaire; le changer en Long id; travaillé

Bonne chance


la source
0

Vous pouvez toujours faire un vidage de session. Flush synchronisera l'état de tous vos objets en session (s'il vous plaît, quelqu'un me corrige si je me trompe), et peut-être que cela résoudrait votre problème dans certains cas.

L'implémentation de vos propres égaux et hashcode peut également vous aider.

caarlos0
la source
0

Vous pouvez vérifier vos paramètres de cascade. Les paramètres Cascade de vos modèles peuvent être à l'origine de ce problème. J'ai supprimé les paramètres de cascade (essentiellement n'autorisant pas les insertions / mises à jour en cascade) et cela a résolu mon problème

user1609848
la source
0

J'ai également trouvé cette erreur. Ce qui a fonctionné pour moi, c'est de m'assurer que la clé primaire (qui est automatiquement générée) n'est pas un PDT (c'est-à-dire long, int, ect.), Mais un objet (c'est-à-dire Long, Integer, etc.)

Lorsque vous créez votre objet pour l'enregistrer, assurez-vous de transmettre null et non 0.

Jaco Van Niekerk
la source
0

est-ce que cela aide?

User userObj1 = new User();
User userObj2 = userObj1;
.
.
.
rtsession.save(userObj1);
rtsession.save(userObj2); 
George Papatheodorou
la source
0

J'ai résolu un problème similaire comme celui-ci:

plan = (FcsRequestPlan) session.load(plan.getClass(), plan.getUUID());
while (plan instanceof HibernateProxy)
    plan = (FcsRequestPlan) ((HibernateProxy) plan).getHibernateLazyInitializer().getImplementation();
user3132194
la source