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


94

Aslında bu konfigürasyonda bazı nesnelerim var (gerçek veri modeli biraz daha karmaşıktır):

  • A'nın B ile çoka çok ilişkisi vardır (B'nin vardır inverse="true")
  • B'nin C ile çoka bir ilişkisi var (ben cascadeayarladım "save-update")
  • C, bir tür tip / kategori tablosudur.

Ayrıca, birincil anahtarların kaydetme sırasında veritabanı tarafından oluşturulduğunu muhtemelen belirtmeliyim.

Verilerimle, bazen A'nın bir dizi farklı B nesnesine sahip olduğu ve bu B nesnelerinin aynı C nesnesine başvurduğu problemlerle karşılaşıyorum.

Ben çağırdığınızda session.saveOrUpdate(myAObject), diyordum bir hazırda hatayı alıyor: "a different object with the same identifier value was already associated with the session: C". Hazırda bekletmenin aynı oturumda aynı nesneyi iki kez ekleyemeyeceğini / güncelleyemeyeceğini / silemeyeceğini biliyorum, ancak bunun etrafında bir yol var mı? Bu pek de alışılmadık bir durum gibi görünmüyor.

Bu problemle ilgili araştırmam sırasında, insanların kullanılmasını önerdiklerini gördüm session.merge(), ancak bunu yaptığımda, "çakışan" nesneler, tüm değerleri null olarak ayarlanmış boş nesneler olarak veritabanına eklenir. Açıkçası istediğimiz bu değil.

[Düzenle] Söylemeyi unuttuğum bir diğer şey de (kontrolüm dışındaki mimari nedenlerden dolayı), her okuma veya yazmanın ayrı bir oturumda yapılması gerektiğidir.


Bu cevabın size yardımcı olup olmadığını görün ..
joaonlima

Yanıtlar:


98

Büyük olasılıkla bunun nedeni, B nesnelerinin aynı Java C nesnesi örneğine başvurmamasıdır. Veritabanındaki aynı satıra (yani aynı birincil anahtara) atıfta bulunurlar ancak farklı kopyalarıdır.

Öyleyse, varlıkları yöneten Hazırda Bekletme oturumu, hangi Java nesnesinin aynı birincil anahtara sahip satıra karşılık geldiğini izleyecektir.

Bir seçenek, aynı satıra başvuran B nesnelerinin Varlıklarının aslında C'nin aynı nesne örneğine atıfta bulunduğundan emin olmak olabilir. Alternatif olarak, bu üye değişkeni için basamaklandırmayı kapatın. Bu şekilde B ısrarcı olduğunda C değildir. Yine de C'yi manuel olarak ayrı ayrı kaydetmeniz gerekecektir. C bir tür / kategori tablosu ise, muhtemelen bu şekilde olması mantıklıdır.


3
Teşekkürler jbx. Dediğiniz gibi, B nesnelerinin bellekteki birden çok C örneğine atıfta bulunduğu ortaya çıktı. Esasen olan şey, programımın bir kısmının C'de okuyor ve onu B'ye ekliyor olması. Başka bir kısım, veritabanından aynı C'ye sahip farklı bir B'yi yüklemektir. Her ikisi de, kaydetme sırasında hatayı tetikleyen A'ya eklenir. B-> C ilişkisi için <pre> basamaklı </pre> ayarını "<pre> yok </pre>" olarak ayarladım, ancak yine de aynı hatayı alıyorum. Çoka bir veya bire çok ilişkilerde, Hibernate'e yalnızca yabancı anahtarı değiştirmesini ve gerisi için endişelenmemesini söylemenin bir yolu var mı?
John

1
C'nin birincil anahtarının herhangi bir kimlik oluşturma stratejisi var mı? Bir dizi oluşturucu veya benzeri bir şey gibi mi?
jbx

Evet, veritabanında her birinin kendi sırası vardır. Bahsettiğiniz gibi, sorun basamaklı hale geldi. Tür tabloları için basamaklamayı kapattık ve diğerleri için, tüm bu boş satırları oluşturmadan merge () 'yi çağırmamıza izin veren "birleştirme" kademesini kullandık. Cevabınızı buna göre işaretledim, teşekkürler!
John

14
SaveOrUpdate () ve BOOM yerine merge () kullandım! işe yarıyor :)
Lahiru Ruhunage


13

Tek bir şey yapman gerekiyor. Çalıştırın session_object.clear()ve ardından yeni nesneyi kaydedin. Bu, oturumu temizler (uygun şekilde adlandırıldığı şekilde) ve sorun teşkil eden yinelenen nesneyi oturumunuzdan kaldırır.


10

@Hemant Kumar'a katılıyorum, çok teşekkür ederim. Onun çözümüne göre sorunumu çözdüm.

Örneğin:

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

Person.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;
    }

}

Bu kod uygulamamda her zaman hata yapar:, A different object with the same identifier value was already associated with the sessiondaha sonra birincil anahtarımı otomatik artırmayı unuttuğumu öğrendim !

Çözümüm, bu kodu birincil anahtarınıza eklemektir:

@GeneratedValue(strategy = GenerationType.AUTO)

6

Bu, tablonuzda aynı nesneye referansla birden çok satırı kaydetmeye çalıştığınız anlamına gelir.

Varlık Sınıfınızın id özelliğini kontrol edin.

@Id
private Integer id;

-e

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

1
verilen açıklamaya göre
sorunlu

5

Aşağıdakileri kullanarak nesne kimliğini Hazırda Bekletme'den veritabanına atama görevini aktarın:

<generator class="native"/>

Bu benim için sorunu çözdü.



3

Yukarıdaki sorunu çözmenin bir yolu hashcode(),.
Ayrıca kaydetmeden önce ve sonra hazırda bekletme oturumunu temizleyin.

getHibernateTemplate().flush();

Ayrılmış nesneyi açıkça ayarlamak nullda yardımcı olur.


2

Az önce bu mesajla karşılaştım ama c # kodunda. Alakalı olup olmadığından emin değilim (yine de tam olarak aynı hata mesajı).

Kodda kesme noktaları ile hata ayıklama yapıyordum ve hata ayıklayıcı bir kesme noktasındayken bazı koleksiyonları özel üyeler aracılığıyla genişletiyordum. Yapıları kazmadan kodu yeniden çalıştırmak hata mesajının kaybolmasına neden oldu. Öyle görünüyor ki, özel tembel yüklenen koleksiyonlara bakma eylemi, NHibernate'in o sırada yüklenmemesi gereken şeyleri yüklemesini sağladı (çünkü özel üyelerdeydi).

Kodun kendisi, bu işlemin (içe aktarma işlemi) bir parçası olarak çok sayıda kaydı ve birçok bağımlılığı güncelleyebilen oldukça karmaşık bir işlemle sarılmıştır.

Umarım sorunla karşılaşan herhangi biri için bir ipucu.


2

Hazırda Bekletme'de "Basamakla" özelliğini bulun ve silin. Mevcut "Basamakla" ayarını yaptığınızda, ilgili sınıflarla ilişkisi olan başka varlıklar üzerindeki diğer işlemleri (kaydetme, güncelleme ve silme) çağıracaktır. Böylece aynı kimlik değeri gerçekleşecek. Benimle çalıştı.


1

Bu hatayı birkaç gün önce aldım ve bu hatayı düzeltmek için çok fazla zaman geçirdim.

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

Bu hatayı almadan önce OrderDetil Nesnesinde ID oluşturma türünden bahsetmemiştim. Orderdetails kimliği üretilmediği zaman, Id'yi her OrderDetail nesnesi için 0 olarak tutar. #jbx bunu açıkladı. Evet, en iyi cevap bu. bu nasıl olduğu bir örnek.


1

Sorgunuzun kodunu öncesine yerleştirmeyi deneyin. Bu benim sorunumu çözer. örneğin bunu değiştirin:

query1 
query2 - get the error 
update

buna:

query2
query1
update

0

güncelleme sorgusunu çağırmadan önce nesnenin tanımlayıcısını ayarlamıyor olabilirsiniz.


3
Olmasaydı bu sorunu olmazdı. Sorun, aynı tanımlayıcıya sahip iki nesneye sahip olmasıdır.
aalku

0

Aşağıdaki gibi bir satır eklediğimde birincil anahtar oluşturmanın yanlış olması nedeniyle sorunla karşılaştım:

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

Kimlik oluşturucu sınıfını kimliğe değiştiriyorum

<id name="id" type="int">
    <column name="id" />
    <generator class="identity"  />
 </id>

0

Benim durumumda sadece flush () işe yaramadı. Flush () sonrasında clear () kullanmak zorunda kaldım.

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


0

IDE'mde açık bırakılan bir ifade sekmesi bu istisnaya neden olan nesnede hazırda bekletme çağrısı yapıyordu. Aynı nesneyi silmeye çalışıyordum. Ayrıca silme çağrısında, bu hatanın gerçekleşmesi için gerekli görünen bir kesme noktam vardı. Ön sekme olarak başka bir ifadeler sekmesi yapmak veya ide'nin kesme noktalarında durmaması için ayarı değiştirmek bu sorunu çözdü.


0

Emin olun, varlığınızın tüm Eşlenen Varlıklarla aynı Üretim Türüne sahip olduğundan emin olun

Ör: UserRole

public class UserRole extends AbstractDomain {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String longName;

private String shortName;

@Enumerated(EnumType.STRING)
private CommonStatus status;

private String roleCode;

private Long level;

@Column(columnDefinition = "integer default 0")
private Integer subRoleCount;

private String modification;

@ManyToOne(fetch = FetchType.LAZY)
private TypeOfUsers licenseType;

}

Modül:

public class Modules implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String longName;

private String shortName;

}

Eşleştirmeli Ana Varlık

public class RoleModules implements Serializable{

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private UserRole role;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private Modules modules;

@Type(type = "yes_no")
private boolean isPrimaryModule;

public boolean getIsPrimaryModule() {
    return isPrimaryModule;
}

}


0

Önceki tüm yanıtlara ek olarak, sınıflarınız için bir Değer Nesnesi kullanmanız VO Transformer sınıfında id niteliğini ayarlamazsa, büyük ölçekli bir projede bu soruna olası bir düzeltme.


0

sadece mevcut işlemi taahhüt et.

currentSession.getTransaction().commit();

şimdi başka bir İşlem başlatabilir ve varlık üzerinde herhangi bir şey yapabilirsiniz


0

Aynı hata mesajının oluşturulabildiği başka bir durum, özel allocationSize:

@Id
@Column(name = "idpar")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "paramsSequence")
@SequenceGenerator(name = "paramsSequence", sequenceName = "par_idpar_seq", allocationSize = 20)
private Long id;

eşleşmeden

alter sequence par_idpar_seq increment 20;

ekleme sırasında kısıt doğrulamasına neden olabilir (anlaşılması kolay) veya "aynı tanımlayıcı değerine sahip farklı bir nesne zaten oturumla ilişkilendirilmişti" - bu durum daha az açıktı.

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.