org.hibernate.PersistentObjectException: अलग की गई इकाई बनी रहती है


89

मैंने अपने पहले मास्टर चाइल्ड उदाहरण को हाइबरनेट के साथ सफलतापूर्वक लिखा था। कुछ दिनों के बाद मैंने इसे फिर से लिया और कुछ पुस्तकालयों को अपग्रेड किया। मुझे यकीन नहीं है कि मैंने क्या किया लेकिन मैं इसे फिर से नहीं चला सका। क्या कोई व्यक्ति यह पता लगाने में मदद करेगा कि त्रुटि संदेश के बाद कोड में क्या गलत है?

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)

हाइबरनेट मानचित्रण:

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

संपादित करें: 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;
    }
}

संपादित करें: JSON क्लाइंट से भेजी गई वस्तु:

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

संपादित करें: कुछ विवरण:
मैंने दो तरीकों का पालन करके चालान को बचाने की कोशिश की है:

  1. मैन्युअल रूप से ऊपर उल्लेखित जन्स ऑब्जेक्ट को गढ़ा गया और इसे सर्वर के नए सत्र में पास किया गया। इस मामले में कॉलिंग विधि को कॉल करने से पहले बिल्कुल कोई गतिविधि नहीं की गई है, इसलिए किसी भी खुले सत्र को बचाने के तरीके को छोड़कर कोई खुला सत्र नहीं होना चाहिए

  2. GetInvoice विधि का उपयोग करके मौजूदा डेटा को लोड किया गया और कुंजी मूल्य को हटाने के बाद उन्होंने उसी डेटा को पारित कर दिया। यह भी मेरा मानना ​​है कि बचत से पहले सत्र को बंद कर देना चाहिए क्योंकि लेनदेन getInvoice विधि में किया जा रहा है।

दोनों ही मामलों में मुझे वही त्रुटि संदेश मिल रहा है जो मुझे यह मानने पर मजबूर कर रहा है कि हाइबरनेट कॉन्फ़िगरेशन फ़ाइल या एंटिटी क्लासेस या सेव विधि के साथ कुछ गलत है।

कृपया मुझे बताएं कि क्या मुझे अधिक जानकारी प्रदान करनी चाहिए

जवाबों:


119

आपने कई प्रासंगिक विवरण प्रदान नहीं किए हैं इसलिए मैं अनुमान लगाऊंगा कि आपने कॉल किया getInvoiceऔर फिर आपने कुछ ऑब्जेक्ट सेट करने के लिए परिणाम ऑब्जेक्ट का उपयोग किया और इस saveधारणा के साथ कॉल किया कि आपकी ऑब्जेक्ट परिवर्तन सहेजे जाएंगे।

हालांकि, persistऑपरेशन ब्रांड नई क्षणिक वस्तुओं के लिए अभिप्रेत है और यह विफल रहता है यदि आईडी पहले से ही असाइन की गई है। आपके मामले में आप शायद saveOrUpdateइसके बजाय कॉल करना चाहते हैं persist

आप JPA / EJB कोड के साथ कुछ चर्चा और संदर्भ "त्रुटि को बनाए रखने के लिए पारित इकाई" पा सकते हैं


धन्यवाद @Alex Gitelman मैंने अपने मूल प्रश्न के नीचे कुछ विवरण जोड़े हैं। क्या यह मेरी समस्या को समझने में मदद करता है? या कृपया मुझे बताएं कि अन्य विवरण क्या सहायक होंगे।
डब्ल्यूएसके

7
आपके संदर्भ ने मुझे बेवकूफ गलती खोजने में मदद की। मैं "itemId" के लिए अशक्त मूल्य नहीं भेज रहा था जो कि बाल तालिका में प्राथमिक कुंजी है। तो हाइबरनेट यह धारणा बना रहा था कि वस्तु पहले से ही कुछ सत्र में मौजूद है। सलाह के लिए धन्यवाद
WSK

अब मुझे यह त्रुटि मिल रही है: "org.hibernate.PropertyValueException: not-null property संदर्भित एक अशक्त या क्षणिक मान: example.forms.InvoiceItem.invoice"। क्या आप मुझे कुछ संकेत दे सकते हैं? अग्रिम धन्यवाद
WSK

आपको इनवेसिव अवस्था में इनवॉइस रखना होगा, क्षणिक नहीं। इसका मतलब है कि आईडी को पहले से ही इसे सौंपा जाना चाहिए। इसलिए Invoiceपहले सेव करें , तो यह आईडी मिलेगा और फिर सेव InvoiceItem। आप कैस्केडिंग के साथ भी खेल सकते हैं।
एलेक्स गिटेलमैन

13

यहां आपने मूल का उपयोग किया है और प्राथमिक कुंजी को मान प्रदान किया है, मूल प्राथमिक कुंजी में ऑटो उत्पन्न होता है।

इसलिए मुद्दा आ रहा है।


1
यदि आपको लगता है कि आपके पास एक प्रश्न के लिए प्रस्ताव करने के लिए अतिरिक्त जानकारी है जो पहले से ही स्वीकृत उत्तर है, तो कृपया अधिक स्पष्ट विवरण प्रदान करें।
शिकागोरेडॉक्स

8

यह @ManyToOne संबंध में मौजूद है। मैंने केवल CascadeType.PERSIST या CascadeType.ALL के बजाय CascadeType.MERGE का उपयोग करके इस समस्या को हल किया। आशा है कि यह आपकी मदद करता है।

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

समाधान:

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

4

सबसे अधिक समस्या यह है कि आप जिस कोड को हमें यहां दिखा रहे हैं उसके बाहर यह समस्या है। आप ऐसी वस्तु को अद्यतन करने का प्रयास कर रहे हैं जो वर्तमान सत्र से संबद्ध नहीं है। यदि यह इनवॉयस नहीं है, तो शायद यह एक इनवॉयस इटम है जो पहले से ही कायम है, डीबी से प्राप्त किया गया है, किसी प्रकार के सत्र में जीवित रखा गया है और फिर आप इसे एक नए सत्र पर जारी रखने की कोशिश करते हैं। यह नहीं हो सकता। एक सामान्य नियम के रूप में, अपनी बनी हुई वस्तुओं को सत्रों में कभी जीवित न रखें।

समाधान यानी पूरे ऑब्जेक्ट ग्राफ को उसी सत्र से प्राप्त करने के लिए जिसे आप इसे जारी रखने की कोशिश कर रहे हैं। एक वेब वातावरण में इसका मतलब होगा:

  • सत्र को प्राप्त करें
  • उन वस्तुओं को प्राप्त करें जिन्हें आपको अपडेट करने या संघों को जोड़ने की आवश्यकता है। उनकी प्राथमिक कुंजी द्वारा अधिमान्य
  • जरूरत पड़ने पर अलर्ट करें
  • सहेजें / अपडेट / बेदखल / हटाना जो आप चाहते हैं
  • अपना सत्र बंद करें / लेनदेन करें

यदि आप समस्याएँ रखते हैं तो कुछ कोड पोस्ट करें जो आपकी सेवा को कॉल कर रहे हैं।


शुक्रिया @joostschouten जाहिरा तौर पर कॉलिंग पद्धति से पहले खुला सत्र नहीं होना चाहिए जैसा कि मैंने "अधिक विवरण" में उल्लेख किया है कि मैंने अपने मूल प्रश्न के नीचे जोड़ा था। क्या कोई तरीका है जो मैं जाँच सकता हूं कि क्या मैं बचाने की विधि को कॉल करने से पहले कुछ सत्र मौजूद है?
WSK

आपकी धारणा "जाहिरा तौर पर कॉल सेव करने के तरीके से पहले खुला सत्र नहीं होना चाहिए" गलत है। आपके मामले में आप प्रत्येक बचत के चारों ओर एक लेन-देन कर रहे हैं और इसका मतलब है कि खुले सत्र नहीं होने चाहिए और यदि वे किसी काम के नहीं होंगे। आपकी समस्या उस कोड में लगती है जो आपके JSON को हैंडल करता है। यहां आप इनवॉइस आइटम के साथ एक चालान पास करते हैं जो पहले से मौजूद है (उनके पास आईडी है)। इसे null id के साथ पास करें और यह सबसे अधिक काम करेगा। या आपकी सेवा के पास JSON है db से इनवॉइसिट्स प्राप्त करें, इनवॉइस में जोड़ें और उन्हें उसी सत्र में सहेजें जिसे आपने उनसे प्राप्त किया था।
joostschouten

@joostschouten अब मुझे यह त्रुटि मिल रही है: "org.hibernate.PropertyValueException: नहीं-शून्य प्रॉपर्टी एक अशक्त या क्षणिक मान का संदर्भ देती है: example.forms.InvoiceItem.invoice"। क्या आप मुझे कुछ विचार दे सकते हैं? अग्रिम धन्यवाद
WSK

1
यह मेरे लिए एक नया प्रश्न लगता है। आपने हमारे साथ एक महत्वपूर्ण कोड कोड साझा नहीं किया है। कोड जो JSON के साथ काम करता है, आपके मॉडल ऑब्जेक्ट को बनाता है और कॉल जारी रहता है और सहेजता है। यह अपवाद आपको बताता है कि आप एक चालान के साथ इसे जारी रखने की कोशिश कर रहे हैं। जो अधिकारपूर्वक नहीं किया जा सकता है। कृपया उस कोड को पोस्ट करें जो वास्तव में आपके मॉडल ऑब्जेक्ट बनाता है।
joostschouten

@joostschouten यह मेरे लिए समझ में आता है, लेकिन समस्या यह है कि मैं JSON के लिए एक फ्रेमवर्क "qooxdoo" का उपयोग कर रहा हूं और सर्वर पर RPC कॉल बना रहा हूं जहां मेरे पास समान फ्रेमवर्क से RPC सर्वर उपयोगिता स्थापित है। तो सब कुछ फ्रेमवर्क कक्षाओं में लपेटा गया है। हजारों रेखाओं को निकालना और पोस्ट करना व्यावहारिक नहीं हो सकता है। दूसरी ओर हम सर्वर साइड में "TheInvoice" ऑब्जेक्ट देख सकते हैं जो बनाया गया है? या हाइबरनेट डिबग / ट्रेस जानकारी दिखाकर?
डब्ल्यूएसके

2

दो समाधान 1. यदि आप ऑब्जेक्ट को अपडेट करना चाहते हैं तो मर्ज का उपयोग करें। यदि आप नई ऑब्जेक्ट को सहेजना चाहते हैं तो सहेजें का उपयोग करें (सुनिश्चित करें कि पहचान हाइबरनेट या डेटाबेस को इसे उत्पन्न करने के लिए अशक्त है) 3. यदि आप
@OneToOne जैसे मैपिंग का उपयोग कर रहे हैं fetch = FetchType.EAGER, cascade = CascadeType.ALL) @ जॉइनकॉलेज (नाम = "stock_id")

तो CascadeType.ERGE CascadeType.ALL का उपयोग करें

धन्यवाद शाहिद अब्बासी


0

जेपीए के लिए फ़िनिस्टमैन मर्ज () के बजाय फ़िस्ट () का उपयोग करके तय किया गया

EntityManager em = getEntityManager();
    try {
        em.getTransaction().begin();
        em.merge(fieldValue);
        em.getTransaction().commit();
    } catch (Exception e) {
        //do smthng
    } finally {
        em.close();
    }

0

मुझे "वही" समस्या थी क्योंकि मैं लिख रहा था

@GeneratedValue(strategy = GenerationType.IDENTITY)

मैंने उस लाइन को डिलीट कर दिया था, इस समय मुझे इसकी आवश्यकता नहीं है, मैं ऑब्जेक्ट्स के साथ परीक्षण कर रहा था। मुझे लगता है कि यह <generator class="native" />आपके मामले में है

मेरे पास कोई नियंत्रक नहीं है और मेरा एपीआई एक्सेस नहीं किया जा रहा है, यह केवल परीक्षण (फिलहाल) के लिए है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.