org.hibernate.PersistentObjectException: entitas terpisah diteruskan ke persist


90

Saya telah berhasil menulis contoh anak master pertama saya dengan hibernate. Setelah beberapa hari saya mengambilnya lagi dan meningkatkan beberapa perpustakaan. Tidak yakin apa yang saya lakukan tetapi saya tidak pernah bisa membuatnya berjalan lagi. Akankah seseorang membantu saya mengetahui apa yang salah dalam kode yang mengembalikan pesan kesalahan berikut:

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)

pemetaan hibernasi:

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

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

EDIT: Objek JSON dikirim dari klien:

{"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}]}

EDIT: Beberapa detail:
Saya telah mencoba menyimpan invoice dengan dua cara berikut:

  1. Dibuat secara manual objek json yang disebutkan di atas dan meneruskannya ke sesi baru server. Dalam hal ini sama sekali tidak ada aktivitas yang telah dilakukan sebelum memanggil metode simpan sehingga tidak boleh ada sesi terbuka kecuali yang dibuka dalam metode penyimpanan

  2. Memuat data yang ada dengan menggunakan metode getInvoice dan mereka meneruskan data yang sama setelah menghapus nilai kunci. Ini juga saya percaya harus menutup sesi sebelum menyimpan karena transaksi dilakukan dalam metode getInvoice.

Dalam kedua kasus saya mendapatkan pesan kesalahan yang sama yang memaksa saya untuk percaya bahwa ada sesuatu yang salah baik dengan file konfigurasi hibernate atau kelas entitas atau metode penyimpanan.

Beri tahu saya jika saya harus memberikan detail lebih lanjut

Jawaban:


120

Anda tidak memberikan banyak detail yang relevan jadi saya akan menebak bahwa Anda memanggil getInvoicedan kemudian Anda menggunakan objek hasil untuk mengatur beberapa nilai dan memanggil savedengan asumsi bahwa perubahan objek Anda akan disimpan.

Namun, persistoperasi ditujukan untuk objek transien baru dan gagal jika id sudah ditetapkan. Dalam kasus Anda, Anda mungkin ingin menelepon saveOrUpdatedaripada persist.

Anda dapat menemukan beberapa diskusi dan referensi di sini "entitas yang terlepas diteruskan untuk mempertahankan kesalahan" dengan kode JPA / EJB


Terima kasih @Alex Gitelman. Saya telah menambahkan beberapa detail di bagian bawah pertanyaan awal saya. Apakah itu membantu untuk memahami masalah saya? atau beri tahu saya detail lain apa yang bisa membantu.
WSK

7
referensi Anda membantu saya menemukan kesalahan bodoh. Saya tidak mengirimkan nilai null untuk "itemId" yang merupakan kunci utama dalam tabel anak. Jadi hibernate membuat asumsi bahwa objek sudah ada dalam beberapa sesi. Terima kasih atas sarannya
WSK

Sekarang saya mendapatkan kesalahan ini: "org.hibernate.PropertyValueException: properti not-null mereferensikan nilai null atau sementara: example.forms.InvoiceItem.invoice". Bisakah Anda memberi saya sedikit petunjuk? Terima kasih sebelumnya
WSK

Anda harus memiliki Faktur dalam keadaan bertahan, bukan sementara. Itu berarti bahwa id harus sudah ditetapkan padanya. Jadi simpan Invoicedulu, jadi akan mendapat id lalu simpan InvoiceItem. Anda juga bisa bermain dengan cascading.
Alex Gitelman

14

Di sini Anda telah menggunakan asli dan menetapkan nilai ke kunci utama, di kunci primer asli dibuat secara otomatis.

Oleh karena itu masalahnya akan datang.


1
Jika Anda yakin Anda memiliki informasi tambahan untuk ditawarkan untuk pertanyaan yang sudah memiliki jawaban yang diterima, berikan penjelasan yang lebih substansial.
ChicagoRedSox

11

Ini ada dalam hubungan @ManyToOne. Saya memecahkan masalah ini hanya dengan menggunakan CascadeType.MERGE daripada CascadeType.PERSIST atau CascadeType.ALL. Semoga membantu Anda.

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

Larutan:

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

4

Kemungkinan besar masalahnya terletak di luar kode yang Anda tunjukkan kepada kami di sini. Anda mencoba memperbarui objek yang tidak terkait dengan sesi saat ini. Jika bukan Invoice, maka mungkin InvoiceItem yang telah dipertahankan, diperoleh dari db, tetap hidup di beberapa jenis sesi dan kemudian Anda mencoba mempertahankannya di sesi baru. Ini tidak mungkin. Sebagai aturan umum, jangan pernah membiarkan objek yang Anda simpan tetap hidup di seluruh sesi.

Solusinya adalah mendapatkan seluruh grafik objek dari sesi yang sama yang Anda coba pertahankan. Dalam lingkungan web, ini berarti:

  • Dapatkan sesinya
  • Ambil objek yang perlu Anda perbarui atau tambahkan pengaitan. Lebih disukai oleh kunci utama mereka
  • Ubah apa yang dibutuhkan
  • Simpan / perbarui / keluarkan / hapus apa yang Anda inginkan
  • Tutup / lakukan sesi / transaksi Anda

Jika Anda terus mengalami masalah, posting beberapa kode yang memanggil layanan Anda.


Terima kasih @joostschouten. Rupanya seharusnya tidak ada sesi terbuka sebelum memanggil metode simpan seperti yang saya sebutkan di "Lebih Detail" yang saya tambahkan di bagian bawah pertanyaan asli saya. Apakah ada cara agar saya dapat memeriksa apakah ada sesi sebelum saya memanggil metode penyimpanan?
WSK

Asumsi Anda "Tampaknya tidak boleh ada sesi terbuka sebelum memanggil metode penyimpanan" adalah salah. Dalam kasus Anda, Anda membungkus transaksi di sekitar setiap penyimpanan dan dapatkan yang berarti bahwa sesi terbuka tidak boleh terjadi dan jika tidak ada gunanya. Masalah Anda tampaknya ada pada kode yang menangani JSON Anda. Di sini Anda mengirimkan faktur dengan item faktur yang sudah ada (mereka memiliki id). Berikan dengan id nol dan kemungkinan besar akan berhasil. Atau minta layanan Anda menangani JSON untuk mendapatkan invoiceitems dari db, tambahkan ke invoice dan simpan di sesi yang sama dengan saat Anda mendapatkannya.
joostschouten

@joostschouten Sekarang saya mendapatkan kesalahan ini: "org.hibernate.PropertyValueException: properti not-null mereferensikan nilai null atau sementara: example.forms.InvoiceItem.invoice". Bisakah Anda memberi saya beberapa ide? Terima kasih sebelumnya
WSK

1
Ini terdengar seperti pertanyaan baru bagi saya. Anda belum membagikan kepada kami sepotong kode penting. Kode yang berhubungan dengan JSON, menghasilkan objek model Anda dan panggilan tetap dan simpan. Pengecualian ini memberitahu Anda bahwa Anda mencoba untuk mempertahankan invoiceItem dengan Faktur nol. Yang seharusnya tidak bisa dilakukan. Harap posting kode yang benar-benar membangun objek model Anda.
joostschouten

@joostschouten Ini masuk akal bagi saya tetapi masalahnya adalah saya menggunakan kerangka "qooxdoo" untuk JSON dan membuat panggilan RPC ke server di mana saya memiliki utilitas server RPC dari kerangka yang sama yang terpasang. Jadi semuanya dibungkus ke dalam kelas kerangka kerja. Mungkin tidak praktis untuk mengekstrak dan memposting ribuan baris. Di sisi lain kita bisa melihat objek "theInvoice" di sisi server yang telah dibuat? atau dengan menampilkan info debug / trace hibernasi?
WSK

2

Dua solusi 1. gunakan penggabungan jika Anda ingin memperbarui objek 2. gunakan simpan jika Anda ingin menyimpan objek baru (pastikan identitas null agar hibernasi atau database menghasilkannya) 3. jika Anda menggunakan pemetaan seperti
@OneToOne ( fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn (name = "stock_id")

Kemudian gunakan CascadeType.ALL ke CascadeType.MERGE

terima kasih Shahid Abbasi


1

Saya memiliki masalah yang "sama" karena saya sedang menulis

@GeneratedValue(strategy = GenerationType.IDENTITY)

Saya menghapus baris itu karena saya tidak membutuhkannya saat ini, saya sedang menguji dengan objek dan sebagainya. Saya pikir itu <generator class="native" />dalam kasus Anda

Saya tidak memiliki pengontrol dan API saya tidak sedang diakses, ini hanya untuk pengujian (saat ini).


0

Untuk JPA diperbaiki menggunakan EntityManager merge () bukan persist ()

EntityManager em = getEntityManager();
    try {
        em.getTransaction().begin();
        em.merge(fieldValue);
        em.getTransaction().commit();
    } catch (Exception e) {
        //do smthng
    } finally {
        em.close();
    }
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.