org.hibernate.PersistentObjectException: kalıcı varlık geçirilen ayrılmış varlık


90

İlk ana çocuk örneğimi hazırda bekletme ile başarıyla yazdım. Birkaç gün sonra tekrar aldım ve bazı kütüphaneleri yükselttim. Ne yaptım emin değilim ama bir daha asla koşturamadım. Biri, aşağıdaki hata mesajını döndüren kodda neyin yanlış olduğunu anlamama yardımcı olur mu:

org.hibernate.PersistentObjectException: detached entity passed to persist: example.forms.InvoiceItem
    at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:127)
    at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:799)
    at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:791)
    .... (truncated)

eşlemeyi hazırda bekletme:

<hibernate-mapping package="example.forms">
    <class name="Invoice" table="Invoices">
        <id name="id" type="long">
            <generator class="native" />
        </id>
        <property name="invDate" type="timestamp" />
        <property name="customerId" type="int" />
        <set cascade="all" inverse="true" lazy="true" name="items" order-by="id">
            <key column="invoiceId" />
            <one-to-many class="InvoiceItem" />
        </set>
    </class>
    <class name="InvoiceItem" table="InvoiceItems">
        <id column="id" name="itemId" type="long">
            <generator class="native" />
        </id>
        <property name="productId" type="long" />
        <property name="packname" type="string" />
        <property name="quantity" type="int" />
        <property name="price" type="double" />
        <many-to-one class="example.forms.Invoice" column="invoiceId" name="invoice" not-null="true" />
    </class>
</hibernate-mapping>

DÜZENLE: InvoiceManager.java

class InvoiceManager {

    public Long save(Invoice theInvoice) throws RemoteException {
        Session session = HbmUtils.getSessionFactory().getCurrentSession();
        Transaction tx = null;
        Long id = null;
        try {
            tx = session.beginTransaction();
            session.persist(theInvoice);
            tx.commit();
            id = theInvoice.getId();
        } catch (RuntimeException e) {
            if (tx != null)
                tx.rollback();
            e.printStackTrace();
            throw new RemoteException("Invoice could not be saved");
        } finally {
            if (session.isOpen())
                session.close();
        }
        return id;
    }

    public Invoice getInvoice(Long cid) throws RemoteException {
        Session session = HbmUtils.getSessionFactory().getCurrentSession();
        Transaction tx = null;
        Invoice theInvoice = null;
        try {
            tx = session.beginTransaction();
            Query q = session
                    .createQuery(
                            "from Invoice as invoice " +
                            "left join fetch invoice.items as invoiceItems " +
                            "where invoice.id = :id ")
                    .setReadOnly(true);
            q.setParameter("id", cid);
            theInvoice = (Invoice) q.uniqueResult();
            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
        } finally {
            if (session.isOpen())
                session.close();
        }
        return theInvoice;
    }
}

Invoice.java

public class Invoice implements java.io.Serializable {

    private Long id;
    private Date invDate;
    private int customerId;
    private Set<InvoiceItem> items;

    public Long getId() {
        return id;
    }

    public Date getInvDate() {
        return invDate;
    }

    public int getCustomerId() {
        return customerId;
    }

    public Set<InvoiceItem> getItems() {
        return items;
    }

    void setId(Long id) {
        this.id = id;
    }

    void setInvDate(Date invDate) {
        this.invDate = invDate;
    }

    void setCustomerId(int customerId) {
        this.customerId = customerId;
    }

    void setItems(Set<InvoiceItem> items) {
        this.items = items;
    }
}

InvoiceItem.java

public class InvoiceItem implements java.io.Serializable {

    private Long itemId;
    private long productId;
    private String packname;
    private int quantity;
    private double price;
    private Invoice invoice;

    public Long getItemId() {
        return itemId;
    }

    public long getProductId() {
        return productId;
    }

    public String getPackname() {
        return packname;
    }

    public int getQuantity() {
        return quantity;
    }

    public double getPrice() {
        return price;
    }

    public Invoice getInvoice() {
        return invoice;
    }

    void setItemId(Long itemId) {
        this.itemId = itemId;
    }

    void setProductId(long productId) {
        this.productId = productId;
    }

    void setPackname(String packname) {
        this.packname = packname;
    }

    void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    void setPrice(double price) {
        this.price = price;
    }

    void setInvoice(Invoice invoice) {
        this.invoice = invoice;
    }
}

DÜZENLE: İstemciden gönderilen JSON nesnesi:

{"id":null,"customerId":3,"invDate":"2005-06-07T04:00:00.000Z","items":[
{"itemId":1,"productId":1,"quantity":10,"price":100},
{"itemId":2,"productId":2,"quantity":20,"price":200},
{"itemId":3,"productId":3,"quantity":30,"price":300}]}

DÜZENLEME: Bazı ayrıntılar:
İki yolu izleyerek faturayı kaydetmeye çalıştım:

  1. Yukarıda bahsedilen json nesnesi manuel olarak üretildi ve onu sunucunun yeni oturumuna geçirdi. Bu durumda, kaydetme yöntemini çağırmadan önce kesinlikle hiçbir etkinlik yapılmamıştır, bu nedenle kaydetme yönteminde açılan dışında herhangi bir açık oturum olmamalıdır.

  2. GetInvoice yöntemi kullanılarak mevcut veriler yüklendi ve anahtar değeri kaldırılarak aynı veriler aktarıldı. Bu da getInvoice yönteminde işlem yapıldığından kaydetmeden önce oturumu kapatması gerektiğine inanıyorum.

Her iki durumda da, beni hazırda bekletme yapılandırma dosyası veya varlık sınıfları veya kaydetme yöntemiyle ilgili bir sorun olduğuna inanmaya zorlayan aynı hata mesajını alıyorum.

Lütfen daha fazla ayrıntı vermem gerekip gerekmediğini bana bildirin

Yanıtlar:


120

Pek çok ilgili ayrıntı sağlamadınız, bu yüzden aradığınızı getInvoiceve daha sonra bazı değerleri ayarlamak için sonuç nesnesini kullandığınızı ve savenesne değişikliklerinizin kaydedileceğini varsayarak çağırdığınızı tahmin ediyorum .

Ancak, persistişlem yepyeni geçici nesneler için tasarlanmıştır ve zaten id atanmışsa başarısız olur. Senin durumunda muhtemelen saveOrUpdateyerine aramak istersin persist.

Burada JPA / EJB koduyla "kalıcı hataya geçirilen ayrılmış varlık" şeklinde bazı tartışmalar ve referanslar bulabilirsiniz.


Teşekkürler @Alex Gitelman. Orijinal sorumun altına bazı ayrıntılar ekledim. Sorunumu anlamama yardımcı oluyor mu? veya lütfen başka hangi ayrıntıların yardımcı olacağını bildirin.
WSK

7
Referansın aptalca bir hata bulmama yardım etti. Alt tablodaki birincil anahtar olan "itemId" için boş değer göndermiyordum. Yani hazırda bekletme, nesnenin bir oturumda zaten var olduğunu varsayıyordu. Tavsiye için teşekkür ederim
WSK

Şimdi şu hatayı alıyorum: "org.hibernate.PropertyValueException: not-null özelliği boş veya geçici bir değere başvuruyor: example.forms.InvoiceItem.invoice". Lütfen bana biraz ipucu verir misin? Şimdiden teşekkürler
WSK

Faturanızın geçici değil kalıcı durumda olması gerekir. Bu, kimliğin zaten atanmış olması gerektiği anlamına gelir. Öyleyse Invoiceönce kaydedin , böylece kimliği alacak ve sonra kaydedecektir InvoiceItem. Basamaklı olarak da oynayabilirsiniz.
Alex Gitelman

14

Burada yerel kullandınız ve birincil anahtara değer atadınız, yerel birincil anahtarda otomatik olarak oluşturulur.

Dolayısıyla konu geliyor.


1
Zaten kabul edilmiş bir cevabı olan bir soru için sunabileceğiniz ek bilgileriniz olduğuna inanıyorsanız, lütfen daha kapsamlı bir açıklama sağlayın.
ChicagoRedSox

11

Bu @ManyToOne ilişkisinde var. Bu sorunu CascadeType.PERSIST veya CascadeType.ALL yerine CascadeType.MERGE kullanarak çözdüm. Umarım size yardımcı olur.

@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="updated_by", referencedColumnName = "id")
private Admin admin;

Çözüm:

@ManyToOne(cascade = CascadeType.MERGE)
@JoinColumn(name="updated_by", referencedColumnName = "id")
private Admin admin;

4

Büyük olasılıkla sorun, bize burada gösterdiğiniz kodun dışında kalıyor. Mevcut oturumla ilişkili olmayan bir nesneyi güncellemeye çalışıyorsunuz. Fatura değilse, o zaman belki zaten ısrarlı, db'den elde edilmiş, bir çeşit oturumda canlı tutulmuş ve sonra yeni bir oturumda devam ettirmeye çalışan bir InvoiceItem'dir. Bu mümkün değil. Genel bir kural olarak, kalıcı nesnelerinizi asla oturumlar boyunca canlı tutmayın.

Çözüm, yani, devam ettirmeye çalıştığınız aynı oturumdan tüm nesne grafiğini elde etmektir. Bir web ortamında bu şu anlama gelir:

  • Oturumu edinin
  • Güncellemeniz veya ilişkilendirmeler eklemeniz gereken nesneleri getirin. Preferabley birincil anahtarına göre
  • Gerekeni değiştirin
  • İstediğinizi kaydedin / güncelleyin / çıkarın / silin
  • Oturumunuzu / işleminizi kapatın / tamamlayın

Sorun yaşamaya devam ederseniz, hizmetinizi arayan kodun bir kısmını gönderin.


Teşekkürler @joostschouten. Görünüşe göre, orijinal sorumun altına eklediğim "Daha Fazla Ayrıntı" da bahsettiğim gibi kaydetme yöntemini çağırmadan önce açık oturum olmamalı. Kaydetme yöntemini çağırmadan önce bazı oturumların olup olmadığını kontrol etmenin bir yolu var mı?
WSK

"Görünüşe göre kaydetme yöntemini çağırmadan önce açık oturum olmamalı" varsayımınız yanlış. Sizin durumunuzda, her kaydetme ve alma işleminin etrafına bir işlem sarıyorsunuz, bu da açık oturumların gerçekleşmemesi gerektiği ve eğer kullanılmayacakları anlamına gelir. Sorununuz JSON'nuzu işleyen kodda görünüyor. Burada, zaten mevcut olan fatura kalemlerini içeren bir faturayı iletirsiniz (kimliklerine sahiptirler). Boş kimliklerle geçirin ve büyük olasılıkla çalışacaktır. Ya da JSON'u yöneten servisinizin fatura öğelerini veritabanından almasını, faturaya eklemesini ve bunları aldığınız oturumda kaydetmesini sağlayın.
joostschouten

@joostschouten Şimdi şu hatayı alıyorum: "org.hibernate.PropertyValueException: not-null özelliği boş veya geçici bir değere başvuruyor: example.forms.InvoiceItem.invoice". Bana biraz fikir verir misin lütfen? Şimdiden teşekkürler
WSK

1
Bu bana yeni bir soru gibi geliyor. Bizimle önemli bir kod parçası paylaşmadınız. JSON ile ilgilenen kod, model Nesnelerinizi oluşturur ve çağrılar devam eder ve kaydedilir. Bu istisna, null Fatura ile bir invoiceItem'i kalıcı kılmaya çalıştığınızı söyler. Hangi haklı olarak yapılamaz. Lütfen model nesnelerinizi gerçekten oluşturan kodu gönderin.
joostschouten

@joostschouten Bu bana mantıklı geliyor, ancak sorun şu ki JSON için bir "qooxdoo" çerçevesi kullanıyorum ve aynı çerçeveden kurulu bir RPC sunucu yardımcı programım olan sunucuya RPC çağrısı oluşturuyorum. Yani her şey çerçeve sınıflarına sarılmıştır. Binlerce satırı çıkarmak ve postalamak pratik olmayabilir. Öte yandan oluşturulan "theInvoice" nesnesini sunucu tarafında izleyebilir miyiz? veya hazırda bekletme hata ayıklama / izleme bilgilerini göstererek mi?
WSK

2

İki çözüm 1. Nesneyi güncellemek istiyorsanız birleştirmeyi kullanın 2. Yeni nesneyi kaydetmek istiyorsanız kaydet'i kullanın (hazırda bekletme veya veritabanının oluşturmasına izin vermek için kimliğin boş olduğundan emin olun) 3.
@OneToOne gibi eşleme kullanıyorsanız ( fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn (name = "stock_id")

Ardından CascadeType.ALL'u CascadeType.MERGE için kullanın

teşekkürler Shahid Abbasi


1

"Aynı" problemi yaşıyordum çünkü yazıyordum

@GeneratedValue(strategy = GenerationType.IDENTITY)

Şu anda ihtiyacım olmadığı için o satırı sildim, nesnelerle test ediyordum vb. Bence <generator class="native" />senin durumunda

Herhangi bir denetleyicim yok ve API'ma erişilmiyor, sadece test için (şu anda).


0

Kalıcı () yerine EntityManager merge () kullanılarak sabitlenen JPA için

EntityManager em = getEntityManager();
    try {
        em.getTransaction().begin();
        em.merge(fieldValue);
        em.getTransaction().commit();
    } catch (Exception e) {
        //do smthng
    } finally {
        em.close();
    }
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.