Hazırda Bekletme Hatası: org.hibernate.NonUniqueObjectException: aynı tanımlayıcı değerine sahip farklı bir nesne oturumla zaten ilişkilendirilmiş


114

İki kullanıcı Nesnem var ve nesneyi kullanarak kaydetmeye çalışırken

session.save(userObj);

Aşağıdaki hatayı alıyorum:

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]

Kullanarak oturumu oluşturuyorum

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

Ben de session.clear()kaydetmeden önce yapmayı denedim , hala şans yok.

Bu ilk kez bir kullanıcı isteği geldiğinde oturum nesnesini alıyorum, bu yüzden neden nesnenin oturumda mevcut olduğunu söylediğinizi anlıyorum.

Baska öneri?


İşte sorunumu çözmeme
Reddymails

Yanıtlar:


173

Bu hatayı birçok kez yaşadım ve izini sürmek oldukça zor olabilir ...

Temel olarak, hazırda bekletmenin söylediği şey, aynı tanımlayıcıya (aynı birincil anahtar) sahip olan ancak aynı nesne olmayan iki nesneye sahip olduğunuzdur.

Kodunuzu parçalamanızı öneririm, yani hata giderilene kadar bitleri yorumlayın ve sonra geri gelene kadar kodu geri koyun ve hatayı bulmalısınız.

Çoğunlukla, A ve B nesneleri arasında kademeli bir kaydetmenin olduğu, ancak B nesnesi zaten oturumla ilişkilendirilmiş, ancak A'daki B ile aynı B örneğinde bulunmadığında, basamaklı kaydetmeler yoluyla gerçekleşir.

Hangi birincil anahtar oluşturucuyu kullanıyorsunuz?

Sormamın nedeni, bu hatanın bir nesnenin kalıcı durumunu (yani bir nesnenin kalıcı olup olmadığını) tespit etmek için hazırda bekletme moduna nasıl geçtiğinizle ilgili olmasıdır. Hata, hazırda bekletme halihazırda kalıcı olan bir nesneyi devam ettirmeye çalıştığı için oluyor olabilir. Aslında, kaydetmeyi hazırda bekletmeyi kullanırsanız, o nesneyi dener ve devam ettirir ve belki de oturumla ilişkilendirilmiş aynı birincil anahtara sahip bir nesne zaten vardır.

Misal

Birincil anahtar kombinasyonuna (sütun 1 ve sütun 2) dayalı 10 satırlık bir tablo için hazırda bekleme sınıf nesneniz olduğunu varsayarsak. Şimdi, bir noktada tablodan 5 satırı kaldırdınız. Şimdi, aynı 10 satırı tekrar eklemeye çalışırsanız, hazırda bekletme, nesneleri veritabanında tutmaya çalışırsa, zaten kaldırılmış olan 5 satır hatasız olarak eklenecektir. Şimdi zaten var olan kalan 5 satır bu istisnayı atacak.

Dolayısıyla, kolay yaklaşım, bir tablodaki bir şeyin parçası olan herhangi bir değeri güncelleyip kaldırmadığınızı ve daha sonra aynı nesneleri tekrar eklemeye çalışıp çalışmadığınızı kontrol etmektir.


4
Güzel Cevap². Birincil anahtar benim sorunumdu, GeneratedValue postgresql için bir sıra ayarlayarak çözüldü.
Rodrigo Ferrari

2
Ben de aynı sorunu yaşadım. Benim durumumda, bir kodda aranan bir nesne vardı ve ilk nesne henüz hazırda bekletme oturumundayken başka bir kod parçasında aynı kimliğe sahip yeni bir nesne oluşturmaya çalışıyorum.
dellasavia

18

Bu, hazırda bekletmenin çözdüğünden daha fazla sorun çıkardığı noktalardan biridir. Benim durumumda aynı tanımlayıcıya 0 sahip birçok nesne var, çünkü bunlar yeniler ve bir tanımlayıcıya sahip değiller. DB onları üretir. Bir yerde okudum ki 0 sinyal Id ayarlanmadı. Onları sürdürmenin sezgisel yolu, onları tekrarlamak ve nesneleri kurtarmak için hazırda bekletme demektir. Ama bunu yapamazsınız - "Elbette, hazırda bekletme modunun şu şekilde ve bu şekilde çalıştığını bilmelisiniz, bu yüzden yapmanız gerekir .." Bu yüzden, şimdi uzun yerine Kimlikleri Uzun olarak değiştirmeyi deneyebilir ve işe yarayıp yaramadığına bakabilirim. Sonunda, basit bir haritacı ile kendi başınıza yapmak daha kolaydır, çünkü hazırda bekletme sadece şeffaf olmayan ek bir yüktür. Başka bir örnek: Bir veritabanındaki parametreleri okumaya ve onları başka bir veritabanında saklamaya çalışmak, sizi neredeyse tüm işleri manuel olarak yapmaya zorlar.


13
Ben de hazırda bekletmekten nefret ediyorum ... ve veritabanları ... Bana neden oldukları tüm sorunlardan sonra sanırım bir metin dosyası kullanmak daha kolay (şaka yapıyorum, ama yine de ...).
Igor Popov

Benim durumumda aynı tanımlayıcıya 0 sahip birçok nesne var, çünkü bunlar yeniler ve bir tanımlayıcıya sahip değiller. DB onları üretir. Bir yerde okudum ki 0 sinyal Id ayarlanmadı. Onları sürdürmenin sezgisel yolu, onları tekrarlamak ve nesneleri kurtarmak için hazırda bekletme demektir . Bunu tam olarak yapmam gerekiyor. Bunu yapmanın "kış uykusuna yatma yolu" hangisi olduğunu bana söyleyebilir misiniz?
Ramses

Benim durumumda, bu nesnenin iki örneğine sahip olduğum için session.merge (nesnem) kullanmak zorunda kaldım. Bu genellikle bir varlık kalıcı olduğunda ve oturumdan ayrıldığında gerçekleşir. Bu varlığın başka bir örneğinin hazırda bekletilmesi isteniyor. Bu ikinci örnek oturuma bağlı kaldı. İlk örnek değiştirildi. Getj2ee.over-blog.com/…
Reddymails

Hazırda bekletme modunda sorun
ACV

17

USe Yöntemin session.evict(object);işlevi, evict()örneği oturum önbelleğinden kaldırmak için kullanılır. Bu nedenle, nesneyi ilk kez kaydederken, nesneyi session.save(object)önbellekten çıkarmadan önce yöntemi çağırarak nesneyi kaydedin . Aynı şekilde nesneyi evict () çağırarak session.saveOrUpdate(object)veya session.update(object)önce güncelleyin .


11

Bu, okuma ve yazma için aynı oturum nesnesini kullandığınızda olabilir. Nasıl? Bir oturum oluşturduğunuzu varsayalım. Emp_id = 101 birincil anahtarıyla çalışan tablosundan bir kayıt okudunuz. Şimdi Java'da kaydı değiştirdiniz. Ve Çalışan kaydını veri tabanına kaydedeceksiniz. burada hiçbir yerde oturum kapatmadık. Okunan nesne de oturumda kaldığı için. Yazmak istediğimiz nesneyle çelişiyor. Dolayısıyla bu hata gelir.


9

Yukarıda birisinin daha önce işaret ettiği gibi cascade=all, bir one-to-manyilişkinin her iki ucunda da varken bu problemle karşılaştım , öyleyse A -> B (A'dan birden çoka ve B'den çoka bir) varsayalım ve A'da B ve ardından saveOrUpdate (A) çağrısı, döngüsel bir kaydetme isteğiyle sonuçlanıyordu, yani A'nın tasarrufunu tetikleyen B'den tasarruf eden A tetikleyicilerinin kaydedilmesi ve üçüncü durumda (A'nın) varlığı denendiğinde sessionPersistenceContext'e eklenecek duplicateObject istisnası atıldı.
  Cascade'i bir uçtan kaldırarak çözebilirim.



5

session.merge(obj)Aynı tanımlayıcı kalıcı nesneyle farklı oturumlarla kaydetme yapıyorsanız kullanabilirsiniz .
İşe yaradı, daha önce aynı sorunu yaşadım.


4

Ben de bu problemle karşılaştım ve hatayı bulmakta zorlandım.

Yaşadığım sorun şuydu:

Nesne, farklı bir hazırda bekletme oturumuna sahip bir Dao tarafından okundu.

Bu istisnadan kaçınmak için, daha sonra bu nesneyi kaydedecek / güncelleyecek olan dao ile nesneyi yeniden okuyun.

yani:

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

}

Umarım bazı insanlara çok zaman kazandırır!


4

Bu problemle karşılaştım:

  1. Bir nesneyi silme (HQL kullanarak)
  2. Aynı kimliğe sahip yeni bir nesnenin hemen depolanması

Silme işleminden sonra sonuçları temizleyerek ve yeni nesneyi kaydetmeden önce önbelleği temizleyerek sorunu çözdüm

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

4

Bu sorun, nesneyi veritabanından almak için kullandığımız aynı oturum nesnesini güncellediğimizde ortaya çıkar.

Güncelleme yöntemi yerine birleştirme hazırda bekletme yöntemini kullanabilirsiniz.

Örneğin, önce session.get () kullanın ve ardından session.merge (nesne) kullanabilirsiniz. Bu yöntem herhangi bir sorun yaratmayacaktır. Veritabanındaki nesneyi güncellemek için merge () yöntemini de kullanabiliriz.


3

Nesneyi oturumun içine alın, işte bir örnek:

MyObject ob = null;
ob = (MyObject) session.get(MyObject.class, id);

3
İyi deneme, ancak "ob" nesnesi güncellenmiş veriler olmadan döndürülür (çalışma süresi sırasında, veritabanından alınan nesneler ile kaydedilmesi arasında uygulama aracılığıyla güncellendiği dikkate alındığında).
Alex

2

Kimlik eşlemeleriniz doğru mu? Veritabanı kimliği bir tanımlayıcı aracılığıyla oluşturmaktan sorumluysa, kullanıcı nesnenizi buna eşlemeniz gerekir ..


2

Bu sorunla bir nesneyi silerken karşılaştım, ne boşaltma ne de net yardım.

/**
 * 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);
}

2

tekrar eden nesnelerin başladığı konumdan önce oturumu kapatmalı ve ardından yeni bir oturum başlatmalısınız.

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

bu şekilde bir oturumda aynı tanımlayıcıya sahip birden fazla varlık yoktur.


2

Partiye geç, ancak gelecek kullanıcılar için yardımcı olabilir -

İ zaman bu sorunu var seçmek kullanarak bir kaydı getsession() tekrar güncellemek aynı olan başka bir rekora tanımlayıcı kullanarak aynı oturumu sorunu neden olur. Aşağıya kod eklendi.

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

Bu asla yapılmamalıdır. Çözüm, güncellemeden önce oturumu boşaltmak veya iş mantığını değiştirmektir.


1

@Id sütununa @GenerateValue koymayı unutup unutmadığınızı kontrol edin. Film ve Tür arasındaki birçok ilişkiyle aynı sorunu yaşadım. Program Hazırda Bekletme Hatası verdi: org.hibernate.NonUniqueObjectException: aynı tanımlayıcı değerine sahip farklı bir nesne zaten oturum hatasıyla ilişkilendirildi. Daha sonra, GenreId alma yöntemine @GenerateValue sahip olduğunuzdan emin olmam gerektiğini öğrendim.


çözümünüzü soruma nasıl uygulayabilirim? Task sınıfında bir kimlik sütunu oluşturabilir miyim? stackoverflow.com/questions/55732955/…
sbattou

1

sadece boş mu yoksa 0 mı aldığını kontrol et

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

içeriğin formdan Pojo'ya ayarlandığı yerde ekleme veya güncelleme


1

NHibernate'te yeniyim ve sorunum, nesnemi sorgulamak için onu kaydetmek için yaptığımdan farklı bir oturum kullanmamdı. Yani kaydetme oturumu nesneyi bilmiyordu.

Açık görünüyor, ancak önceki cevapları okurken her yerde 2 seans değil 2 nesne arıyordum.


1

@GeneratedValue (strateji = GenerationType.IDENTITY), bu ek açıklamayı varlık çekirdeğinizdeki birincil anahtar özelliğine eklemek bu sorunu çözmelidir.


1

Bu sorunu çözdüm.
Aslında bu, bean sınıfında Generator Type PK özelliğinin uygulanmasını unuttuğumuz için oluyor. Öyleyse istediğiniz gibi yapın

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

Bean nesnelerini ısrarla sürdürdüğümüzde, her nesne aynı ID'yi elde etti, bu nedenle ilk nesne kaydedilir, başka bir nesne kalıcı olduğunda daha sonra Exception: org.hibernate.NonUniqueObjectException:aynı tanımlayıcı değerine sahip bu tür farklı bir nesne aracılığıyla HIB FW zaten oturumla ilişkilendirildi.


1

Sorun, aynı hazırda bekletme oturumunda aynı tanımlayıcıya sahip iki nesneyi kaydetmeye çalıştığınız için ortaya çıkar.İki çözüm vardır: -

  1. Bu, mapping.xml dosyanızı aşağıdaki gibi kimlik alanları için doğru şekilde yapılandırmadığınız için oluyor: -

    <id name="id">
      <column name="id" sql-type="bigint" not-null="true"/>
      <generator class="hibernateGeneratorClass"</generator>
    </id>
  2. İsSessionClear gibi bir Parametreyi kabul etmek için alma yöntemini aşırı yükleyin ve mevcut oturumu aşağıdaki gibi döndürmeden önce oturumu temizleyin

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

Bu, mevcut oturum nesnelerinin temizlenmesine neden olur ve hazırda bekletme benzersiz bir tanımlayıcı oluşturmasa bile, veritabanınızı Auto_Increment gibi bir şey kullanarak bir birincil anahtar için doğru şekilde yapılandırdığınızı varsayarsak, sizin için çalışmalıdır.


1

Benzer bir problemim vardı. Benim durumumda ayarlamak için unutmuştu increment_bykullandığı gibi aynı olmasını veritabanında değerini cache_sizeve allocationSize. (Oklar belirtilen nitelikleri gösterir)

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 <-)

1

Aksi olandan wbdarby bir nesne, bir HQL için nesnenin tanımlayıcı vererek getirilen olduğunda söyleyen, hatta gerçekleşebilir. Nesne alanlarını değiştirmeye ve tekrar DB'ye kaydetmeye (değişiklik eklenebilir, silinebilir veya güncellenebilir) aynı oturum üzerinde denenmesi durumunda bu hata görünecektir. Değiştirilen nesnenizi kaydetmeden önce hazırda bekletme oturumunu temizlemeyi deneyin veya yeni bir oturum oluşturun.

Umarım yardım etmişimdir ;-)


1

Setimi Jackson'dan yeni bir setle değiştirirken aynı hatayı alıyorum.

Bunu çözmek için mevcut seti tutuyorum, eski setten bilinmeyen elemanı ile yeni listeye kaldırıyorum retainAll. Sonra yenilerini ile ekliyorum addAll.

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

Oturuma sahip olmaya ve onu manipüle etmeye gerek yok.


1

Bunu dene. Aşağıdakiler benim için çalıştı!

Gelen hbm.xmldosyanın

  1. dynamic-updateSınıf etiketinin özniteliğini şu şekilde ayarlamamız gerekiyor true:

    <class dynamic-update="true">
  2. Benzersiz sütunun altındaki oluşturucu etiketinin sınıf özelliğini şu şekilde ayarlayın identity:

    <generator class="identity">

Not: Benzersiz sütunu identityyerine olarak ayarlayın assigned.


0

Benim için çalışan başka bir şey de uzun yerine Uzun örnek değişkenini yapmaktı.


Birincil anahtar değişkenimin uzun kimliğine sahiptim; Uzun id olarak değiştirme; işlenmiş

Herşey gönlünce olsun


0

Her zaman bir seans floş yapabilirsiniz. Flush, oturumdaki tüm nesnelerinizin durumunu senkronize eder (lütfen hatalıysam birisi beni düzeltir) ve belki bazı durumlarda sorununuzu çözebilir.

Kendi eşitlik ve hashcode'unuzu uygulamak da size yardımcı olabilir.


0

Basamaklama Ayarlarınızı kontrol edebilirsiniz. Modellerinizdeki Cascade ayarları buna neden olabilir. Basamaklı Ayarlarını kaldırdım (Esasen Basamaklı Eklemelere / Güncellemelere izin vermiyorum) ve bu sorunumu çözdü


0

Ben de bu hatayı buldum. Benim için işe yarayan şey, birincil anahtarın (yani otomatik olarak oluşturulan) bir PDT (yani uzun, kesirli, vb.) Değil, bir nesne (ör. Uzun, Tamsayı vb.)

Nesnenizi kaydetmek için oluşturduğunuzda, 0'ı değil null değerini ilettiğinizden emin olun.


0

Bu yardımcı olur mu?

User userObj1 = new User();
User userObj2 = userObj1;
.
.
.
rtsession.save(userObj1);
rtsession.save(userObj2); 

0

Buna benzer bir sorunu çözdüm:

plan = (FcsRequestPlan) session.load(plan.getClass(), plan.getUUID());
while (plan instanceof HibernateProxy)
    plan = (FcsRequestPlan) ((HibernateProxy) plan).getHibernateLazyInitializer().getImplementation();
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.