Fehler im Ruhezustand: org.hibernate.NonUniqueObjectException: Der Sitzung wurde bereits ein anderes Objekt mit demselben Bezeichnerwert zugeordnet

114

Ich habe zwei Benutzerobjekte und während ich versuche, das Objekt mit zu speichern

session.save(userObj);

Ich erhalte die folgende Fehlermeldung:

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]

Ich erstelle die Sitzung mit

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

Ich habe auch versucht, das session.clear()vor dem Speichern zu tun , immer noch kein Glück.

Dies ist das erste Mal, dass ich das Sitzungsobjekt erhalte, wenn eine Benutzeranforderung eingeht. Daher erhalte ich den Grund, warum dieses Objekt in der Sitzung vorhanden ist.

Irgendwelche Vorschläge?

hart
quelle
Hier ist ein weiterer wunderbarer Thread, der zur Lösung meines Problems beigetragen hat. Getj2ee.over-blog.com/…
Reddymails

Antworten:

173

Ich habe diesen Fehler schon oft gehabt und es kann ziemlich schwierig sein, ihn aufzuspüren ...

Grundsätzlich bedeutet der Ruhezustand, dass Sie zwei Objekte haben, die dieselbe Kennung (denselben Primärschlüssel) haben, aber nicht dasselbe Objekt sind.

Ich würde vorschlagen, dass Sie Ihren Code aufschlüsseln, dh Bits auskommentieren, bis der Fehler behoben ist, und dann den Code zurücksetzen, bis er zurückkommt und Sie den Fehler finden sollten.

Dies geschieht meistens über kaskadierende Speicherungen, bei denen zwischen Objekt A und B eine Kaskadenspeicherung stattfindet, Objekt B jedoch bereits mit der Sitzung verknüpft wurde, sich jedoch nicht in derselben Instanz von B befindet wie die auf A.

Welchen Primärschlüsselgenerator verwenden Sie?

Der Grund, den ich frage, ist, dass dieser Fehler damit zusammenhängt, wie Sie den Ruhezustand anweisen, den dauerhaften Zustand eines Objekts zu ermitteln (dh ob ein Objekt persistent ist oder nicht). Der Fehler kann auftreten, weil der Ruhezustand versucht, ein bereits beständiges Objekt beizubehalten. Wenn Sie den Ruhezustand speichern verwenden, wird versucht, dieses Objekt beizubehalten, und möglicherweise ist der Sitzung bereits ein Objekt mit demselben Primärschlüssel zugeordnet.

Beispiel

Angenommen, Sie haben ein Klassenobjekt im Ruhezustand für eine Tabelle mit 10 Zeilen basierend auf einer Primärschlüsselkombination (Spalte 1 und Spalte 2). Jetzt haben Sie zu einem bestimmten Zeitpunkt 5 Zeilen aus der Tabelle entfernt. Wenn Sie nun erneut versuchen, dieselben 10 Zeilen hinzuzufügen, während im Ruhezustand versucht wird, die Objekte in der Datenbank beizubehalten, werden 5 bereits entfernte Zeilen ohne Fehler hinzugefügt. Jetzt lösen die verbleibenden 5 Zeilen, die bereits vorhanden sind, diese Ausnahme aus.

Der einfache Ansatz wäre also zu überprüfen, ob Sie einen Wert in einer Tabelle aktualisiert / entfernt haben, der Teil von etwas ist, und später versuchen Sie, dieselben Objekte erneut einzufügen

Michael Wiles
quelle
4
Schöne Antwort². Der Primärschlüssel war mein Problem, das mit dem GeneratedValue gelöst wurde, der eine Sequenz für postgresql festlegte.
Rodrigo Ferrari
2
Ich hatte das gleiche Problem. In meinem Fall wurde ein Objekt in einem Code durchsucht und ich habe versucht, ein neues Objekt mit derselben ID in einem anderen Code zu erstellen, während sich das erste Objekt noch im Ruhezustand befand.
Dellasavia
18

Dies ist nur ein Punkt, an dem der Ruhezustand mehr Probleme verursacht als löst. In meinem Fall gibt es viele Objekte mit derselben Kennung 0, da sie neu sind und keine haben. Die Datenbank generiert sie. Irgendwo habe ich gelesen, dass 0 Signale nicht gesetzt sind. Die intuitive Möglichkeit, sie beizubehalten, besteht darin, sie zu durchlaufen und den Ruhezustand zu aktivieren, um die Objekte zu speichern. Aber das können Sie nicht tun - "Natürlich sollten Sie wissen, dass der Ruhezustand so und so funktioniert, deshalb müssen Sie ..." Jetzt kann ich versuchen, die IDs in "Lang" anstatt "Lang" zu ändern und zu prüfen, ob es dann funktioniert. Am Ende ist es einfacher, dies mit einem einfachen Mapper selbst zu tun, da der Ruhezustand nur eine zusätzliche intransparente Belastung darstellt. Ein weiteres Beispiel: Wenn Sie versuchen, Parameter aus einer Datenbank zu lesen und in einer anderen zu speichern, müssen Sie fast alle Arbeiten manuell ausführen.

Hein
quelle
13
Ich hasse auch den Winterschlaf ... und Datenbanken ... Nach all den Problemen, die sie mir verursacht haben, ist es wohl einfacher, eine Textdatei zu verwenden (ich mache Witze, aber immer noch ...).
Igor Popov
In meinem Fall gibt es viele Objekte mit derselben Kennung 0, weil sie neu sind und keine haben. Die Datenbank generiert sie. Irgendwo habe ich gelesen, dass 0 Signale nicht gesetzt sind. Die intuitive Möglichkeit, sie beizubehalten, besteht darin, sie zu durchlaufen und den Ruhezustand zu aktivieren, um die Objekte zu speichern . Ich muss genau das tun. Können Sie mir sagen, wie dies im "Ruhezustand" geschehen soll?
Ramses
In meinem Fall musste ich session.merge (myobject) verwenden, da ich zwei Instanzen dieses Objekts hatte. Normalerweise passiert dies, wenn eine Entität beibehalten und von der Sitzung getrennt wird. Eine andere Instanz dieser Entität wird zum Ruhezustand aufgefordert. Diese zweite Instanz blieb mit der Sitzung verbunden. Die erste Instanz wird geändert. Mehr unter getj2ee.over-blog.com/…
Reddymails
Es ist kein Winterschlaf, der Probleme macht, aber Unverständnis darüber, wie es funktioniert
ACV
17

USe session.evict(object);Mit der Funktion evict()method wird die Instanz aus dem Sitzungscache entfernt. Speichern Sie das Objekt zum ersten Mal, indem Sie die session.save(object)Methode aufrufen , bevor Sie das Objekt aus dem Cache entfernen. Auf die gleiche Weise aktualisieren Sie das Objekt durch Aufrufen session.saveOrUpdate(object)oder session.update(object)vor dem Aufruf von evict ().

Rahul N.
quelle
11

Dies kann passieren, wenn Sie dasselbe Sitzungsobjekt zum Lesen und Schreiben verwendet haben. Wie? Angenommen, Sie haben eine Sitzung erstellt. Sie lesen einen Datensatz aus der Mitarbeitertabelle mit dem Primärschlüssel Emp_id = 101 Jetzt haben Sie den Datensatz in Java geändert. Und Sie werden den Mitarbeiterdatensatz in der Datenbank speichern. Wir haben hier nirgendwo eine Sitzung geschlossen. Da das gelesene Objekt auch in der Sitzung bestehen bleibt. Es widerspricht dem Objekt, das wir schreiben möchten. Daher kommt dieser Fehler.

Prashant Bari
quelle
9

Wie bereits oben erwähnt, bin ich auf dieses Problem gestoßen, als ich cascade=allan beiden Enden einer one-to-manyBeziehung war. Nehmen wir also A -> B (eins zu viele von A und viele zu eins von B) an und aktualisiere die Instanz von B in A und dann das Aufrufen von saveOrUpdate (A) führte zu einer zirkulären Speicheranforderung, dh das Speichern von A löst das Speichern von B aus, das das Speichern von A auslöst ... und in dritter Instanz, als die Entität (von A) versucht wurde dem sessionPersistenceContext hinzugefügt werden, wurde die duplicateObject-Ausnahme ausgelöst.
  Ich könnte es lösen, indem ich die Kaskade von einem Ende entferne.

redzedi
quelle
Ich habe mein Problem zusammen mit stackoverflow.com/questions/4334970/… gelöst
Magno C
5

Sie können verwenden session.merge(obj), wenn Sie mit verschiedenen Sitzungen mit demselben persistenten Bezeichnerobjekt speichern.
Es hat funktioniert, ich hatte vorher das gleiche Problem.

Pavan Kumar
quelle
4

Ich bin auch auf dieses Problem gestoßen und hatte Schwierigkeiten, den Fehler zu finden.

Das Problem, das ich hatte, war das folgende:

Das Objekt wurde von einem Dao mit einer anderen Ruhezustandssitzung gelesen.

Um diese Ausnahme zu vermeiden, lesen Sie das Objekt einfach erneut mit dem Dao, das dieses Objekt später speichern / aktualisieren soll.

so:

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();
    }

}

Hoffe, dass einige Leute viel Zeit sparen!

Frithjof
quelle
4

Ich bin auf dieses Problem gestoßen durch:

  1. Objekt löschen (mit HQL)
  2. Sofortiges Speichern eines neuen Objekts mit derselben ID

Ich habe es behoben, indem ich die Ergebnisse nach dem Löschen geleert und den Cache geleert habe, bevor ich das neue Objekt gespeichert habe

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

Dieses Problem tritt auf, wenn wir dasselbe Sitzungsobjekt aktualisieren, mit dem wir das Objekt aus der Datenbank abgerufen haben.

Sie können die Zusammenführungsmethode für den Ruhezustand anstelle der Aktualisierungsmethode verwenden.

Beispiel: Verwenden Sie zuerst session.get () und dann session.merge (Objekt). Diese Methode verursacht keine Probleme. Wir können auch die merge () -Methode verwenden, um das Objekt in der Datenbank zu aktualisieren.

Jayendra Birtharia
quelle
3

Holen Sie sich das Objekt in die Sitzung, hier ein Beispiel:

MyObject ob = null;
ob = (MyObject) session.get(MyObject.class, id);
sock_osg
quelle
3
Netter Versuch, aber das Objekt "ob" wird ohne die aktualisierten Daten zurückgegeben (wenn man bedenkt, dass es zur Laufzeit durch die Anwendung zwischen den aus der Datenbank abgerufenen Objekten und deren Speicherung aktualisiert wurde).
Alex
2

Sind Ihre ID-Zuordnungen korrekt? Wenn die Datenbank für die Erstellung der ID über eine Kennung verantwortlich ist, müssen Sie Ihr Benutzerobjekt diesem zuordnen.

cwap
quelle
2

Ich bin auf dieses Problem beim Löschen eines Objekts gestoßen, das weder geräumt noch eindeutig geholfen hat.

/**
 * 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
quelle
2

Vor der Position, an der sich wiederholende Objekte beginnen, sollten Sie die Sitzung schließen und anschließend eine neue Sitzung starten

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

Auf diese Weise gibt es in einer Sitzung nicht mehr als eine Entität mit derselben Kennung.

engtuncay
quelle
2

Spät zur Party, kann aber für kommende User helfen -

Ich habe dieses Problem , wenn ich wählen Sie einen Datensatz mit getsession() und wieder aktualisieren einen anderen Datensatz mit derselben Kennung mit derselben Sitzung das Problem verursacht. Code unten hinzugefügt.

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

Dies sollte niemals getan werden. Die Lösung besteht entweder darin, die Sitzung vor dem Update zu entfernen oder die Geschäftslogik zu ändern.

vipin cp
quelle
1

Überprüfen Sie, ob Sie vergessen haben, die Spalte @GenerateValue für @Id einzufügen. Ich hatte das gleiche Problem mit vielen zu vielen Beziehungen zwischen Film und Genre. Das Programm hat den Ruhezustand ausgelöst: org.hibernate.NonUniqueObjectException: Dem Sitzungsfehler wurde bereits ein anderes Objekt mit demselben Bezeichnerwert zugeordnet. Ich fand später heraus, dass ich nur sicherstellen muss, dass Sie @GenerateValue für die GenreId get-Methode haben.

Thupten
quelle
Wie kann ich Ihre Lösung auf meine Frage anwenden? erstelle ich eine ID-Spalte in der Task-Klasse? stackoverflow.com/questions/55732955/…
sbattou
1

Überprüfen Sie einfach die ID, ob es null oder 0 wie nimmt

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

Hinzufügen oder Aktualisieren, wenn der Inhalt vom Formular in Pojo festgelegt wird

Ayan Sarkar
quelle
1

Ich bin neu in NHibernate und mein Problem war, dass ich mein Objekt in einer anderen Sitzung abgefragt habe als zum Speichern. Die Speichersitzung wusste also nichts über das Objekt.

Es scheint offensichtlich, aber nach dem Lesen der vorherigen Antworten habe ich überall nach 2 Objekten gesucht, nicht nach 2 Sitzungen.

DustinA
quelle
1

@GeneratedValue (Strategie = GenerationType.IDENTITY) Durch Hinzufügen dieser Anmerkung zur Primärschlüsseleigenschaft in Ihrer Entity-Bean sollte dieses Problem behoben werden.

Jay
quelle
1

Ich habe dieses Problem gelöst.
Tatsächlich geschieht dies, weil wir die Implementierung der PK-Eigenschaft Generator Type in der Bean-Klasse vergessen haben. Also mach es zu einem beliebigen Typ wie

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

Wenn wir die Objekte von Bean beibehalten, hat jedes Objekt dieselbe ID erhalten, sodass das erste Objekt gespeichert wird. Wenn ein anderes Objekt beibehalten Exception: org.hibernate.NonUniqueObjectException:werden soll, wurde der Sitzung bereits HIB FW über diesen Typ eines anderen Objekts mit demselben Bezeichnerwert zugeordnet.

Ankit Sharma
quelle
1

Das Problem tritt auf, weil Sie in derselben Ruhezustandssitzung versuchen, zwei Objekte mit derselben Kennung zu speichern. Es gibt zwei Lösungen:

  1. Dies geschieht, weil Sie Ihre Mapping.xml-Datei für ID-Felder wie folgt nicht richtig konfiguriert haben: -

    <id name="id">
      <column name="id" sql-type="bigint" not-null="true"/>
      <generator class="hibernateGeneratorClass"</generator>
    </id>
  2. Überladen Sie die Methode getsession, um einen Parameter wie isSessionClear zu akzeptieren, und löschen Sie die Sitzung, bevor Sie die aktuelle Sitzung wie unten beschrieben zurückgeben

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

Dies führt dazu, dass vorhandene Sitzungsobjekte gelöscht werden. Auch wenn im Ruhezustand keine eindeutige Kennung generiert wird, sollte dies für Sie funktionieren, vorausgesetzt, Sie haben Ihre Datenbank mithilfe von Auto_Increment ordnungsgemäß für einen Primärschlüssel konfiguriert.

Rahul Kumar
quelle
1

Ich hatte ein ähnliches Problem. In meinem Fall hatte ich vergessen, den increment_byWert in der Datenbank auf den gleichen Wert wie den von cache_sizeund festzulegen allocationSize. (Die Pfeile zeigen auf die genannten Attribute)

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
quelle
1

Anders als von wbdarby gesagt, kann es sogar passieren, wenn ein Objekt abgerufen wird, indem die Kennung des Objekts einem HQL übergeben wird. Wenn Sie versuchen, die Objektfelder zu ändern und sie in derselben Sitzung wieder in der Datenbank zu speichern (Änderung kann Einfügen, Löschen oder Aktualisieren sein) , wird dieser Fehler angezeigt. Versuchen Sie, die Ruhezustandssitzung zu löschen, bevor Sie Ihr geändertes Objekt speichern, oder erstellen Sie eine brandneue Sitzung.

Hoffe ich habe geholfen ;-)

Arash Moradabadi
quelle
1

Ich habe den gleichen Fehler, bei dem ich mein Set durch ein neues von Jackson ersetzt habe.

Um dies zu lösen, behalte ich die vorhandene Menge bei und entferne aus der alten Menge das unbekannte Element in die neue Liste mit retainAll. Dann füge ich die neuen mit hinzu addAll.

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

Sie müssen die Sitzung nicht haben und sie manipulieren.

ÔRel
quelle
1

Versuche dies. Das Folgende hat bei mir funktioniert!

In der hbm.xmlDatei

  1. Wir müssen das dynamic-updateAttribut des Klassen-Tags auf Folgendes setzen true:

    <class dynamic-update="true">
  2. Setzen Sie das Klassenattribut des Generator-Tags unter der eindeutigen Spalte auf identity:

    <generator class="identity">

Hinweis: Setzen Sie die eindeutige Spalte auf identityanstatt auf assigned.

RaGa__M
quelle
0

Eine andere Sache, die für mich funktioniert hat, war, die Instanzvariable Long anstelle von long zu machen


Ich hatte meine Primärschlüsselvariable lange ID; Ändern in Long id; hat funktioniert

Alles Gute


quelle
0

Sie können jederzeit eine Sitzungsspülung durchführen. Flush synchronisiert den Status aller Ihrer Objekte in der Sitzung (bitte korrigiert mich jemand, wenn ich falsch liege), und möglicherweise wird es in einigen Fällen Ihr Problem lösen.

Das Implementieren Ihrer eigenen Gleichheits- und Hashcodes kann Ihnen ebenfalls helfen.

caarlos0
quelle
0

Sie können Ihre Kaskadeneinstellungen überprüfen. Die Cascade-Einstellungen Ihrer Modelle könnten dies verursachen. Ich habe die Cascade-Einstellungen entfernt (Cascade Inserts / Updates sind im Wesentlichen nicht zulässig), wodurch mein Problem behoben wurde

user1609848
quelle
0

Ich habe diesen Fehler auch gefunden. Was für mich funktioniert hat, ist sicherzustellen, dass der Primärschlüssel (der automatisch generiert wird) kein PDT (dh long, int, ect.) Ist, sondern ein Objekt (dh Long, Integer usw.).

Stellen Sie beim Erstellen Ihres Objekts zum Speichern sicher, dass Sie null und nicht 0 übergeben.

Jaco Van Niekerk
quelle
0

Hilft das?

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

Ich habe ein ähnliches Problem wie dieses gelöst:

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