2014-12-19 8 views
6

को जारी रखने के लिए पारित की गई है मुझे अपवाद प्राप्त हो रहा है। Eibernate.PersistentObjectException: अलग इकाई को जारी रखने के लिए पारित किया गया। इस मंच और अन्य जगहों पर तमाम पद से, मैं समझता हूँ कि इस दो मामलों में होता है (पर विचार नहीं एक-एक एनोटेशन आदि),इंटरमीटेंट त्रुटि org.hibernate.PersistentObjectException: पृथक इकाई

  1. वहाँ लेनदेन गुंजाइश
  2. एक आईडी से बाहर जाने के साथ एक मुद्दा है सेट है जहां इसे स्वचालित रूप से जेनरेट किया जाना चाहिए।

मुझे लगता है कि इनमें से कोई भी मेरे कोड के साथ नहीं हो रहा है। मैं त्रुटि को पुन: उत्पन्न करने में असमर्थ हूं, क्योंकि मेरे पास डेटा नहीं है जिसने शुरुआत में इसे ट्रिगर किया था। अन्य डेटा पर यह पूरी तरह से ठीक चलाता है। मैं नीचे एक SCCE प्रदान की है:

public class MyProcessor { 
    private MyImportEJB myEJB = MyImportEJB.getInstance(); 
    private List<MyClass> saveQueue = new ArrayList<MyClass>(); 
    public void process() { 
     List<X> rawData = getListOfX(); 
     for(X x:rawData) { 
      processX(); 
     } 
     saveFoos(saveQueue); 
    } 

    public void saveOrUpdateFoos(List<Foo> foos) { 

     for(MyClass foo:foos) { 
       MyClass existingFoo = myEJB.getFoosForWidAndDates(foo.getWid(), foo.getEffBeginDt(),foo.getEffEndDt()); 
       if(existingFoo == null) saveQueue.add(foo); 
       else { 
        existingFoo.updateIfDifferent(foo); 
        saveQueue.add(existingFoo); 
       } 
     } 

     if(saveQueue.size() > 5000) { 
      myEJB.saveObjects(saveQueue); 
      saveQueue.clear(); 
     } 
    } 

    public void processX() { 
      ArrayList<MyClass> foos = new ArrayList<MyClass>(); 

      if(x.reportPeriod != null && x.gravity != null){ 
       MyClass foo = new MyClass(); 
       foo.setId(null); 
       foo.setWId(x.getWid()); 
       foo.setEffBeginDt(x.reportPeriod); 
       foo.setEffEndDt(addOneMonth(x.reportPeriod)); 
       foo.setGravity(x.gravity); 

       foos.add(foo); 
      } 
      saveOrUpdateFoos(foos); 

    } 
} 

MyImportEJB.java:

@Stateless 
@EJB(name = "MyImportEJB", beanInterface = MyImportEJB.class) 
@TransactionAttribute(TransactionAttributeType.SUPPORTS) 
@PermitAll 
public class MyImportEJB{ 
    @Override 
    @TransactionAttribute(TransactionAttributeType.REQUIRED) 
    public void saveObjects(List<? extends P> mappedObjects) 
    { 
     for (P mappedObject : mappedObjects) 
     { 
      this.saveObject(mappedObject); 
     } 
    } 


    @Override 
    @TransactionAttribute(TransactionAttributeType.REQUIRED) 
    public void saveObject(P mappedObject) 
    { 
     EntityManager entityManager = this.getEntityManager(); 

     Object identifier = this.getEntityManagerFactory().getPersistenceUnitUtil().getIdentifier(mappedObject); 
     if (identifier != null) { 
      Object existingObject = entityManager.find(mappedObject.getClass(), identifier); 

      if (existingObject != null) { 
       entityManager.merge(mappedObject); 
       return; 
      } 
     } 

     entityManager.persist(mappedObject); 
    } 

    public MyClass getFoosForWidAndDates(Integer wid, Calendar effBeginDt, Calendar effEndDt) { 
     try { 
      return (MyClass)((this.entityManager 
     .createQuery("select M from MyClass M where wid = :wid and effBeginDt = :effBeginDt and effEndDt = :effEndDt ", MyClass.class) 
     .setParameter("wid",wid) 
     .setParameter("effBeginDt", effBeginDt) 
     .setParameter("effEndDt", effEndDt)).getSingleResult()); 
     } catch(NoResultException | NonUniqueResultException e) { 
      return null; 
     } 
    } 
} 

MyClass.java

public MyClass{ 

     @Id 
     @GeneratedValue(strategy=GenerationType.IDENTITY) 
     @Column(name = "Id") 
     private Integer id; 

     @Column(name = "wid") 
     private Integer wId; 

     @Column(name = "eff_begin_dt") 
     private Calendar effBeginDt; 

     @Column(name = "eff_end_dt") 
     private Calendar effEndDt; 

     @Column(name = "gravity") 
     private Double gravity; 

     private Integer dataDownloadId; 

     public void updateIfDifferent(MyClass other) { 
      if(other.gravity != null && other.gravity != this.gravity) this.gravity = other.gravity; 
      //same for effBeginDt andeffEndDt 

     } 

} 

persistence.xml

<?xml version="1.0" encoding="UTF-8" ?> 

<persistence xmlns="http://java.sun.com/xml/ns/persistence" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" 
version="2.0"> 
    <persistence-unit name="ProdData"> 
     <description>ProdData Database Persistence Unit</description> 
     <provider>org.hibernate.ejb.HibernatePersistence</provider> 
     <jta-data-source>java:jboss/ProdDataJNDI</jta-data-source> 
     <class>path.to.MyClass</class> 
     <class>path.to.X</class> 
     <exclude-unlisted-classes>true</exclude-unlisted-classes> 

     <properties> 
      <property name="hibernate.show_sql" value="false" /> 
     </properties> 
    </persistence-unit> 
</persistence> 

अपवाद पर फेंक दिया जाता है कॉलिंग इकाई प्रबंधक .persिस्ट (मैप edObject) < - MyImportEJB.saveObject < -MyImportEJB.saveObjects। मेरे पास लाइन नंबर

मैंने एक नमूना प्रोग्राम लिखने का प्रयास किया है जहां मुझे डेटाबेस से मौजूदा Foo ऑब्जेक्ट मिलता है, अपडेट और सहेजता है, क्योंकि यह त्रुटि का सबसे संभावित स्रोत था। लेकिन मैं त्रुटि को पुन: पेश नहीं कर सका। कृपया मदद करे।

संपादित करें:

@TransactionAttribute(TransactionAttributeType.SUPPORTS) 
public List<Integer> getUnprocessedIds(Class<? extends ProductionRawData> clazz, Integer dataDownloadId) { 
    String canonicalName = clazz.getCanonicalName(); 

    String queryStr = "select id from " + canonicalName + " where datadownloadId = :dataDownloadId and isProcessed != 1"; 

    TypedQuery<Integer> query = this.entityManager.createQuery(queryStr, Integer.class) 
     .setParameter("dataDownloadId", dataDownloadId); 
    try { 
     return query.getResultList(); 
    } catch(NoResultException nre) { 
     return new ArrayList<T>(); 
    } 
} 

संपादित करें:

public List<X> getListOfX() { 
    return myImportEJB.getUnprocessedIds(X.class, 30495); 
} 
फ़ाइल MyImportEJB.java से

: यहाँ getListofX()

अनुरोध किया MyProcessor.java से की जानकारी दी जा रही इसके अलावा जोड़ा getFoosForWidAndDates() के विवरण। यह मुझे सुझाव दिया गया था कि मैंने आईडी को एक नई फू पर सेट कतार में जोड़ने से पहले शून्य पर सेट किया था। मैं जानना चाहूंगा अगर यह संभव है कि आईडी एक अस्वीकार्य मूल्य

+0

getListOfX() का स्रोत क्या है? क्या आपकी इकाई का किसी अन्य इकाई के साथ कोई संबंध है? मुझे लगता है कि यह प्रश्न एक [डुप्लिकेट] है (http://stackoverflow.com/questions/2441598/detached-entity-passed-to-persist-error-with-jpa-ejb-code), यह आपकी मदद कर सकता है। अन्यथा आपके पास [वह अन्य एक्सचेंज] है (http://stackoverflow.com/questions/6378526/org-hibernate-persistentobjectexception-detached-entity-passed-to-persistist), लेकिन यह आपकी स्थिति में फिट नहीं है। – bdulac

+0

जिस इकाई को मैं सहेज रहा हूं उसके किसी भी अन्य इकाई के साथ कोई संबंध नहीं है –

+0

MyImportEJB.getInstance() कैसे काम करता है? यदि 'MyImportEJB' कंटेनर द्वारा नहीं बनाया गया है तो आपके एनोटेशन द्वारा निहित लेनदेन प्रबंधन सेमेन्टिक्स में से कोई भी प्रभाव नहीं पड़ेगा। तथ्य यह है कि आप [@PersistenceContext] (http://docs.oracle.com/javaee/6/api/javax/persistence/PersistenceContext.html) के साथ EntityManager इंजेक्शन नहीं लग रहे हैं, यह मुझे थोड़ा संदिग्ध बनाता है क्योंकि यह सुझाता है वह इंजेक्शन आपके लिए काम नहीं कर रहा था। –

उत्तर

0

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

लेनदेन का समय समाप्त हो गया था और बड़ी संख्या में ऑब्जेक्ट्स को बचाने की कोशिश कर रहा था, जिसके कारण मैं फू ऑब्जेक्ट्स को अलग कर रहा था। किसी भी तरह से TransactionRollbackException पहली बार लॉग में दिखाई नहीं दे रहा था, शायद क्योंकि यह वास्तव में 12: 0am यूटी पर हुआ था। संस्थाओं के लिए नेतृत्व इस सिद्धांत के समर्थन में, कार्यक्रम अब तक दुर्घटनाग्रस्त नहीं हुआ है के बाद मैं बैच का आकार करने के लिए ~ 1000

रुचि रखने वालों के लिए कम, एक TransactionRollbackException करता अलग हो रही है: entityManager.getTransaction().rollback() detaches entities?

वहाँ संभाल करने के कई तरीके हैं यह तब होता है, लेकिन मैंने सादगी के लिए उन्हें लागू करने का विकल्प चुना

0

मैं इससे आपकी समस्या का

if(saveQueue.size() > 5000) { 
    myEJB.saveObjects(saveQueue); 
    saveQueue.clear(); 
} 

आप saveQueue भरने कर रहे हैं हो सकता है सोचने के लिए किया जा रहा है "उन्नत" हाइबरनेट द्वारा 5000 तक, और फिर आप उन्हें जारी/मर्ज करने का प्रयास करते हैं। मुझे हाइबरनेट के बारे में निश्चित नहीं है, लेकिन एक्लिप्ससेंक का डिफ़ॉल्ट साझा कैश आकार 1000 है। इसका मतलब है, आप saveQueue को भर सकते हैं, उदाहरण के लिए, 2000 मौजूदा Foo एस और जब तक आप persist() पर कॉल करते हैं, तो वे अब नहीं हैं इकाई प्रबंधक का कैश। यह अलग-अलग उदाहरण पर persist() पर कॉल करने की ओर जाता है जिसमें id पहले से सेट है।

आपके मामले में, मुझे लगता है कि मौसम की परवाह किए बिना entityManager.merge(mappedObject) को सभी ऑब्जेक्ट्स पर कॉल करना बिल्कुल ठीक होगा, क्योंकि यह मौजूदा वस्तु है या नहीं, क्योंकि यह मौजूदा लोगों को अपडेट करेगा, और नए बने रहेंगे।

+0

मैं कैसे सुनिश्चित कर सकता हूं कि यह मुद्दा है? क्या कहने का कोई तरीका है, कैश आकार कम करें और इसे इस तरह के व्यवहार को ट्रिगर करें? मुझे यह सुनिश्चित करने की ज़रूरत है कि मैंने उत्पादन –

+0

में इसे चलाने से पहले कोड को ठीक कर दिया है, मैंने पहले ही एक्लिप्ससेंक की कैश सेटिंग्स को अतीत में निपटाया है, इसलिए या तो हाइबरनेट इसे अलग तरीके से कर रहा है, या यह समय के बाद से बहुत कुछ बदल गया है मै उसका इस्तेमाल किया। मुझे डिफ़ॉल्ट L2 कैश आकार सेट करने का कोई तरीका नहीं मिल रहा है, मुझे यह भी पुष्टि नहीं मिल सकती है कि यह डिफ़ॉल्ट रूप से सक्षम है। लेकिन, यदि आप 5000+ मौजूदा 'फू' (यदि कोई डिफ़ॉल्ट कैश आकार है, तो मुझे यकीन है कि यह नहीं है> 5000) के साथ एक परीक्षण लिख सकता है, तो आप इस सिद्धांत का परीक्षण कर सकते हैं। अगर परीक्षण काम करता है, तो मैं गलत हूं। –

+0

इसके अलावा, मुझे यकीन नहीं है कि यह क्यों जारी रहेगा()? फ़ंक्शन सेवऑरअपडेट में, यह इकाई मैनेजर.फिंड() को कॉल करने के लिए कॉल कर रहा है यह जांचने के लिए कि डेटाबेस में ऑब्जेक्ट पहले से मौजूद है या नहीं। यदि यह मौजूद है तो यह मर्ज() को कॉल करता है। [इस] के आधार पर (http://stackoverflow.com/questions/1607532/when-to-use-entitymanager-find-vs-entitymanager-getreference) उत्तर, ऐसा लगता है कि खोज कैश में ऑब्जेक्ट की तलाश नहीं करती है लेकिन वास्तव में ऑब्जेक्ट –

0

मुझे आशा है कि आप नवीनतम Hibernate का उपयोग कर रहे हैं। और मैं उम्मीद करते हैं कि इसे यहाँ कोई गलती है:

if(existingFoo == null) saveQueue.add(existingFoo); // if it is a null, you add it? 

अन्यथा यह काफी रोचक लग रहा है, तो आपको निम्न काम करने की कोशिश कर सकते:

  1. प्रत्येक के बाद फ्लश सत्र (10, 1000) मौजूदा वस्तुओं, उस तरह: अगर इकाई पहले से कायम था

    public void saveObjects(List<? extends P> mappedObjects) 
    { 
        for (P mappedObject : mappedObjects) 
        { 
         this.saveObject(mappedObject); 
         // some condition if you have a lot of objects 
         this.entityManager.flush(); 
        } 
    } 
    
  2. उपयोग कम विदेशी रास्ता जाँच करने के लिए:

    public void saveObject(P mappedObject) { 
        EntityManager entityManager = this.getEntityManager(); 
    
        if (mappedObject.getId() != null) { 
         entityManager.merge(mappedObject); 
         return; 
        } 
    
        entityManager.persist(mappedObject); 
    } 
    

    या यह बेहतर पूरी तरह से merge से बचने और बस प्रबंधित वस्तुओं के साथ काम है, यह एक एकल लेनदेन सभी कार्यों के लिए, इस प्रकार बातों का नया स्वरूप की आवश्यकता होगी की आवश्यकता होगी।

+0

मैंने मौजूदा फ़ू के साथ टाइपो को ठीक किया है, इसे वास्तव में foo ऑब्जेक्ट को सहेजना चाहिए। तो मैं आपके सुझावों का प्रयास कर सकता हूं, लेकिन बात यह है कि मैं उत्पादन वातावरण में इसे चलाने से पहले समस्या के मूल कारण की पहचान करना चाहता हूं। किसी कारण से, जो अपवाद फेंक रहा है वह लॉग को भर रहा है, भले ही मेरे पास कैच ब्लॉक में है। यह डिस्क स्पेस बाधाओं के कारण सर्वर को क्रैश कर रहा है। मुझे या तो एक समाधान की आवश्यकता है जो समस्या को सही तरीके से पहचानता है, या इसे पूरी तरह से रोकता है (अपनी प्रबंधित वस्तुओं की तरह कहें) –

संबंधित मुद्दे