जेपीए: बड़े परिणाम सेटों पर पुनरावृत्ति के लिए उचित पैटर्न क्या है?


114

मान लीजिए कि मेरे पास लाखों पंक्तियों वाली एक तालिका है। JPA का उपयोग करना, उस तालिका के विरुद्ध क्वेरी पर पुनरावृति करने का उचित तरीका क्या है, जैसे कि मेरे पास लाखों वस्तुओं के साथ सभी इन-मेमोरी सूची नहीं है ?

उदाहरण के लिए, मुझे संदेह है कि यदि तालिका बड़ी है तो निम्नलिखित को झटका लगेगा:

List<Model> models = entityManager().createQuery("from Model m", Model.class).getResultList();

for (Model model : models)
{
     System.out.println(model.getId());
}

क्या पेजिंग (लूपिंग और मैन्युअल रूप से अपडेट करना setFirstResult()/ setMaxResult()) वास्तव में सबसे अच्छा समाधान है?

संपादित करें : मेरे द्वारा लक्षित प्राथमिक उपयोग-मामला एक प्रकार का बैच कार्य है। यह ठीक है अगर इसे चलाने में लंबा समय लगता है। कोई वेब क्लाइंट शामिल नहीं है; मुझे बस एक बार में प्रत्येक पंक्ति (एक या कुछ छोटे एन) के लिए "कुछ करने" की आवश्यकता है। मैं बस एक ही समय में स्मृति में उन सभी से बचने की कोशिश कर रहा हूं।


क्या डेटाबेस और JDBC ड्राइवर का उपयोग कर रहे हैं?

जवाबों:


55

Hibernate के साथ Java Persistence का पेज 537 उपयोग करके एक समाधान देता है ScrollableResults, लेकिन अफसोस कि यह केवल Hibernate के लिए है।

तो ऐसा लगता है कि setFirstResult/ setMaxResultsऔर मैनुअल पुनरावृत्ति का उपयोग करना वास्तव में आवश्यक है। यहाँ JPA का उपयोग करके मेरा समाधान है:

private List<Model> getAllModelsIterable(int offset, int max)
{
    return entityManager.createQuery("from Model m", Model.class).setFirstResult(offset).setMaxResults(max).getResultList();
}

फिर, इसे इस तरह उपयोग करें:

private void iterateAll()
{
    int offset = 0;

    List<Model> models;
    while ((models = Model.getAllModelsIterable(offset, 100)).size() > 0)
    {
        entityManager.getTransaction().begin();
        for (Model model : models)
        {
            log.info("do something with model: " + model.getId());
        }

        entityManager.flush();
        entityManager.clear();
        em.getTransaction().commit();
        offset += models.size();
    }
}

33
मुझे लगता है कि बैच प्रक्रिया के दौरान नए आवेषण होने पर उदाहरण सुरक्षित नहीं है। उपयोगकर्ता को एक कॉलम के आधार पर ऑर्डर करना होगा जहां यह सुनिश्चित हो कि नव सम्मिलित डेटा परिणाम सूची के अंत में होगा।
बालाज़्स ज़ोल्डोस

जब वर्तमान पृष्ठ अंतिम पृष्ठ होता है और 100 से कम तत्वों की जाँच होती है size() == 100, तो एक अतिरिक्त क्वेरी को छोड़ देगा जो खाली सूची देता है
cdalxndr

38

मैंने यहां प्रस्तुत उत्तरों की कोशिश की, लेकिन JBoss 5.1 + MySQL कनेक्टर / J 5.1.15 + हाइबरनेट 3.3.2 ने उन लोगों के लिए काम नहीं किया। हमने अभी JBoss 4.x से JBoss 5.1 में माइग्रेट किया है, इसलिए हम इसके लिए अभी से रुके हुए हैं, और इस प्रकार हम जो नवीनतम हाइबरनेट का उपयोग कर सकते हैं वह 3.3.2 है।

अतिरिक्त मापदंडों के जोड़े को जोड़ने का काम किया, और इस तरह कोड OOME के ​​बिना चलता है:

        StatelessSession session = ((Session) entityManager.getDelegate()).getSessionFactory().openStatelessSession();

        Query query = session
                .createQuery("SELECT a FROM Address a WHERE .... ORDER BY a.id");
        query.setFetchSize(Integer.valueOf(1000));
        query.setReadOnly(true);
        query.setLockMode("a", LockMode.NONE);
        ScrollableResults results = query.scroll(ScrollMode.FORWARD_ONLY);
        while (results.next()) {
            Address addr = (Address) results.get(0);
            // Do stuff
        }
        results.close();
        session.close();

महत्वपूर्ण लाइनें createQuery और स्क्रॉल के बीच क्वेरी पैरामीटर हैं। उनके बिना "स्क्रॉल" कॉल मेमोरी में सब कुछ लोड करने की कोशिश करता है और या तो कभी खत्म नहीं होता है या आउटऑफ़मैरीऑरर पर चलता है।


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

मदद करने के लिए खुश :-)। हम बड़े डेटा सेट के साथ भी काम कर रहे हैं, इस मामले में उपयोगकर्ता को एक ही शहर / काउंटी या कभी-कभी राज्य के भीतर सभी सड़क नामों को क्वेरी करने की अनुमति मिलती है, इसलिए इंडेक्स बनाने के लिए बहुत अधिक डेटा पढ़ने की आवश्यकता होती है।
Zds

MySQL के साथ प्रकट होता है कि आपको वास्तव में उन सभी हुप्स के माध्यम से जाना है: stackoverflow.com/a/20900045/32453 (अन्य DB की कम कठोर हो सकती है मैं कल्पना करूँगा ...)
rogerdpack

32

आप वास्तव में सीधे जेपीए में ऐसा नहीं कर सकते, हालांकि हाइबरनेट के पास स्टेटलेस सत्र और स्क्रॉल करने योग्य परिणाम सेट के लिए समर्थन है।

हम इसकी मदद से अरबों पंक्तियों की नियमित प्रक्रिया करते हैं।

यहाँ प्रलेखन के लिए एक लिंक है: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/batch.html#batch-statelesssession


17
धन्यवाद। यह जानने में अच्छा है कि कोई व्यक्ति हाइबरनेट के माध्यम से अरबों पंक्तियों को कर रहा है। यहाँ कुछ लोग दावा कर रहे हैं कि यह असंभव है। :-)
जॉर्ज आर्महोल्ड

2
एक उदाहरण यहाँ भी जोड़ना संभव है? मैं इसे Zds के उदाहरण के समान मानता हूं?
रॉगरडपैक

19

ईमानदार होने के लिए, मैं सुझाव दूंगा कि JPA छोड़ दें और JDBC के साथ रहें (लेकिन निश्चित रूप से JdbcTemplateसमर्थन वर्ग या इस तरह का उपयोग करके )। जेपीए (और अन्य ओआरएम प्रदाता / विनिर्देश) को एक लेनदेन के भीतर कई वस्तुओं पर काम करने के लिए डिज़ाइन नहीं किया गया है क्योंकि वे मानते हैं कि भरी हुई सब कुछ प्रथम-स्तरीय कैश में रहना चाहिए (इसलिए clear()जेपीए की आवश्यकता है )।

इसके अलावा, मैं और अधिक निम्न स्तर के समाधान की सिफारिश कर रहा हूं क्योंकि ओआरएम (प्रतिबिंब केवल एक हिमखंड का एक सिरा है) इतना महत्वपूर्ण हो सकता है, कि सादे से अधिक पुनरावृत्ति करना ResultSet, यहां तक ​​कि कुछ हल्के समर्थन का भी उल्लेख किया गया है जैसे कि JdbcTemplateबहुत तेज़ होगा।

JPA को बड़ी मात्रा में संस्थाओं के संचालन के लिए डिज़ाइन नहीं किया गया है। आप से बचने के लिए flush()/ साथ खेल सकते हैं , लेकिन एक बार फिर इस पर विचार करें। आप विशाल संसाधन खपत की कीमत का भुगतान बहुत कम करते हैं।clear()OutOfMemoryError


JPA का लाभ केवल डेटाबेस अज्ञेय नहीं हो रहा है, बल्कि एक पारंपरिक डेटाबेस (NoSQL) का उपयोग करने की संभावना भी नहीं है। हर बार फ्लश / क्लीयर करना कठिन नहीं है और आमतौर पर बैच ऑपरेशन अक्सर किए जाते हैं।
एडम गिंट

1
हाय थॉमसज। मेरे पास जेपीए / हाइबरनेट के बारे में शिकायत करने के बहुत सारे कारण हैं, लेकिन सम्मानजनक रूप से, मुझे वास्तव में संदेह है कि वे "कई वस्तुओं पर काम करने के लिए डिज़ाइन नहीं किए गए हैं"। मुझे संदेह है कि मुझे इस उपयोग-मामले के लिए उचित पैटर्न सीखने की जरूरत है।
जॉर्ज आर्महोल्ड

4
खैर, मैं केवल दो पैटर्न के बारे में सोच सकता हूं: पृष्ठांकन (कई बार उल्लिखित) और flush()/ clear()। पहले एक IMHO बैच प्रसंस्करण के प्रयोजनों के लिए डिज़ाइन नहीं किया गया है, जबकि फ्लश () / स्पष्ट () के रिसाव की तरह गंध का उपयोग करते हुए ।
टॉमाज़ नर्कविक्ज़

हाँ, यह आप के रूप में उल्लेख किया था और आड़ू / स्पष्ट का एक संयोजन था। धन्यवाद!
जॉर्ज आर्महोल्ड

7

यदि आप Iterable के रूप में परिणाम प्राप्त करने के लिए इस विधि का उपयोग करके EclipseLink I का उपयोग करते हैं

private static <T> Iterable<T> getResult(TypedQuery<T> query)
{
  //eclipseLink
  if(query instanceof JpaQuery) {
    JpaQuery<T> jQuery = (JpaQuery<T>) query;
    jQuery.setHint(QueryHints.RESULT_SET_TYPE, ResultSetType.ForwardOnly)
       .setHint(QueryHints.SCROLLABLE_CURSOR, true);

    final Cursor cursor = jQuery.getResultCursor();
    return new Iterable<T>()
    {     
      @SuppressWarnings("unchecked")
      @Override
      public Iterator<T> iterator()
      {
        return cursor;
      }
    }; 
   }
  return query.getResultList();  
}  

करीब की विधि

static void closeCursor(Iterable<?> list)
{
  if (list.iterator() instanceof Cursor)
    {
      ((Cursor) list.iterator()).close();
    }
}

6
नाइस jQuery ऑब्जेक्ट
usr-local-'

मैंने आपके कोड को एक कोशिश दी, लेकिन अभी भी OOM मिलता है - यह सभी T ऑब्जेक्ट्स दिखाई देता है (और सभी टी से संदर्भित टेबल ऑब्जेक्ट्स) GC नहीं हैं। रूपरेखा उन्हें org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork में org.eclipse.persistence.internal.identitymaps.CacheKey के साथ "तालिका" से संदर्भित किया जा रहा है। मैंने कैश में देखा और मेरी सेटिंग्स सभी डिफॉल्ट (डिसेबल सिलेक्टिव, वीक विद सॉफ्ट सबकेच, कैश साइज 100, ड्रॉप इनवैलिडेट) हैं। मैं अक्षम सत्रों में देखूंगा और देखूंगा कि क्या यह मदद करता है। BTW मैं केवल "(T o: results)" का उपयोग करके रिटर्न कर्सर पर पुनरावृति करता हूं।
एडी बाइस

Badum tssssssss
dctremblay

5

यह इस बात पर निर्भर करता है कि आपको किस तरह का ऑपरेशन करना है। आप लाख से अधिक पंक्ति में क्यों लूप कर रहे हैं? क्या आप बैच मोड में कुछ अपडेट कर रहे हैं? क्या आप किसी ग्राहक को सभी रिकॉर्ड प्रदर्शित करने जा रहे हैं? क्या आप पुनः प्राप्त संस्थाओं पर कुछ आँकड़ों की गणना कर रहे हैं?

यदि आप क्लाइंट को एक लाख रिकॉर्ड प्रदर्शित करने जा रहे हैं, तो कृपया अपने यूजर इंटरफेस पर पुनर्विचार करें। इस मामले में, उचित समाधान आपके परिणामों और उपयोग setFirstResult()और setMaxResult()

यदि आपने बड़ी मात्रा में रिकॉर्ड का अपडेट लॉन्च किया है, तो आप अपडेट को सरल और बेहतर बनाए रखेंगे Query.executeUpdate()। वैकल्पिक रूप से, आप संदेश-संचालित बीन ओए काम प्रबंधक का उपयोग करके अद्यतन को अतुल्यकालिक मोड में निष्पादित कर सकते हैं।

यदि आप पुनः प्राप्त संस्थाओं पर कुछ आँकड़ों की गणना कर रहे हैं, तो आप जेपीए विनिर्देश द्वारा परिभाषित समूहीकरण कार्यों का लाभ उठा सकते हैं।

किसी अन्य मामले के लिए, कृपया अधिक विशिष्ट बनें :)


काफी बस, मुझे "प्रत्येक" पंक्ति के लिए कुछ करने की आवश्यकता है। निश्चित रूप से यह एक सामान्य उपयोग का मामला है। अब मैं जिस विशिष्ट मामले में काम कर रहा हूं, मुझे प्रत्येक पंक्ति से एक आईडी (पीके) का उपयोग करके एक बाहरी वेब सेवा को क्वेरी करने की आवश्यकता है जो मेरे डेटाबेस से पूरी तरह से बाहर है। परिणाम किसी भी क्लाइंट वेब ब्राउज़र पर वापस प्रदर्शित नहीं होते हैं, इसलिए बोलने के लिए कोई उपयोगकर्ता इंटरफ़ेस नहीं है। यह एक बैच का काम है, दूसरे शब्दों में।
जॉर्ज आर्महोल्ड

यदि आपको प्रत्येक पंक्ति के लिए "आईडी" प्रिंट करने की आवश्यकता है, तो प्रत्येक पंक्ति प्राप्त करने का कोई अन्य तरीका नहीं है, आईडी और प्रिंट प्राप्त करें। सबसे अच्छा समाधान इस बात पर निर्भर करता है कि आपको क्या करने की आवश्यकता है।
Dainius

@ कैफीन कोमा, यदि आपको केवल प्रत्येक पंक्ति की आईडी की आवश्यकता है, तो सबसे बड़ा सुधार शायद केवल उस कॉलम को लाने से होगा, जैसा कि SELECT m.id FROM Model mऔर फिर एक सूची <Integer> से अधिक है।
जोर्न होर्स्टमन

1
@ जोर्न होर्स्टमन- अगर लाखों पंक्तियाँ हैं, तो क्या यह वास्तव में मायने रखेगा? मेरा कहना है कि लाखों वस्तुओं (हालांकि छोटा) के साथ एक ArrayList JVM ढेर के लिए अच्छा नहीं होने वाला है।
जॉर्ज आर्महोल्ड

@ डैनियस: मेरा सवाल वास्तव में है: "मैं पूरे अर्रे-इन-मेमोरी के बिना प्रत्येक पंक्ति पर कैसे पुनरावृति कर सकता हूं?" दूसरे शब्दों में, मैं एक समय में एन को खींचने के लिए एक इंटरफ़ेस पसंद करूंगा, जहां एन 1 मिलियन से काफी छोटा है। :-)
जॉर्ज आर्महोल्ड

5

ऐसा करने के लिए कोई "उचित" नहीं है, यह वह नहीं है जो JPA या JDO या कोई अन्य ORM करने का इरादा है, सीधे JDBC आपका सबसे अच्छा विकल्प होगा, क्योंकि आप इसे छोटी संख्या में पंक्तियों को वापस लाने के लिए कॉन्फ़िगर कर सकते हैं एक समय और उन्हें फ्लश के रूप में वे उपयोग किया जाता है, यही कारण है कि सर्वर साइड कर्सर मौजूद हैं।

ORM उपकरण बल्क प्रोसेसिंग के लिए डिज़ाइन नहीं किए गए हैं, वे आपको ऑब्जेक्ट्स में हेरफेर करने और RDBMS बनाने का प्रयास करने के लिए डिज़ाइन किए गए हैं जो डेटा को यथासंभव पारदर्शी में संग्रहीत किया जाता है, अधिकांश पारदर्शी भाग में कम से कम कुछ हद तक विफल होते हैं। इस पैमाने पर, सैकड़ों हजारों पंक्तियों (ऑब्जेक्ट्स) को संसाधित करने का कोई तरीका नहीं है, किसी भी ओआरएम के साथ बहुत कम लाखों और यह ऑब्जेक्ट तात्कालिकता उपरि, सादे और सरल होने के कारण किसी भी उचित समय में निष्पादित होता है।

उपयुक्त उपकरण का उपयोग करें। सीधे JDBC और स्टोर की गई प्रक्रियाओं में 2011 में निश्चित रूप से एक स्थान है, विशेष रूप से उन बनाम ORM फ्रेमवर्क में क्या बेहतर है।

किसी भी चीज़ को एक लाख में खींचना, यहां तक ​​कि एक साधारण List<Integer>में बहुत कुशल होने के बावजूद कि आप इसे कैसे करते हैं, इस पर ध्यान नहीं दिया जा रहा है। आप जो पूछ रहे हैं, उसे करने का सही तरीका एक सरल SELECT id FROM table, सेट SERVER SIDE(विक्रेता निर्भर) और उस पर कर्सर FORWARD_ONLY READ-ONLYऔर पुनरावृति करना है।

यदि आप वास्तव में हर एक के साथ कुछ वेब सर्वर को कॉल करने के लिए लाखों आईडी की प्रक्रिया कर रहे हैं, तो आप किसी भी उचित मात्रा में चलाने के लिए कुछ समवर्ती प्रसंस्करण भी करने जा रहे हैं। एक JDBC कर्सर के साथ खींचना और उनमें से कुछ को एक समवर्ती LinkedQueue में रखना और थ्रेड का एक छोटा पूल (# CPU / Cores + 1) खींचना और उन्हें किसी मशीन के साथ अपने कार्य को पूरा करने का एकमात्र तरीका है " RAM की सामान्य "" मात्रा, जिसे देखते हुए आप पहले से ही मेमोरी से बाहर चल रहे हैं।

इस उत्तर को भी देखें ।


1
तो आप कह रहे हैं कि किसी भी कंपनी को कभी भी अपने उपयोगकर्ता तालिका की प्रत्येक पंक्ति पर जाने की आवश्यकता नहीं है? जब वे ऐसा करने का समय आता है तो उनके प्रोग्रामर खिड़की से हाइबरनेट फेंक देते हैं? " वहाँ सैकड़ों हजारों पंक्तियों को संसाधित करने का कोई तरीका नहीं है " - मेरे सवाल में मैंने setFirstResult / setMaxResult बताया है, इसलिए स्पष्ट रूप से एक तरीका है। मैं पूछ रहा हूं कि क्या कोई बेहतर है।
जॉर्ज आर्महोल्ड

"एक साधारण सूची में भी, किसी भी चीज़ के एक लाख को खींचने से <Integer> आप कैसे हैं, इसकी परवाह किए बिना बहुत कुशल नहीं होगा।" बिलकुल मेरी बात। मैं पूछ रहा हूं कि विशाल सूची कैसे नहीं बनाई जाए, बल्कि परिणाम सेट पर पुनरावृति करने के लिए।
जॉर्ज आर्महोल्ड

जैसा कि मैंने अपने उत्तर में सुझाया है, एक सीधे सीधे JDBC का चयन करें एक FORWARD_ONLY READ_ONLY एक SERVER_SIDE कर्सर के साथ स्टेटमेंट का उपयोग करें। JDBC का उपयोग कैसे करें एक SERVER_SIDE कर्सर डेटाबेस ड्राइवर पर निर्भर है।

1
मैं जवाब से पूरी तरह सहमत हूं। सबसे अच्छा समाधान समस्या पर निर्भर है। यदि समस्या कुछ संस्थाओं को आसानी से लोड कर रही है तो जेपीए अच्छा है। यदि समस्या बड़ी मात्रा में डेटा का कुशलता से उपयोग कर रही है तो प्रत्यक्ष रूप से JDBC बेहतर है।
extraneon

4
लाखों कारणों से स्कैन करना कई कारणों से आम है, उदाहरण के लिए उन्हें एक खोज इंजन में अनुक्रमित करना। और यद्यपि मैं इस बात से सहमत हूं कि JDBC आम तौर पर एक अधिक प्रत्यक्ष मार्ग है, आप कभी-कभी एक ऐसी परियोजना में चलते हैं जिसमें पहले से ही बहुत जटिल व्यावसायिक तर्क है जो हाइबरनेट परत में बंधा हुआ है। यदि आप इसे दरकिनार करते हैं और JDBC में जाते हैं, तो आप व्यवसाय तर्क को दरकिनार कर देते हैं, जो कि कभी-कभी गैर-तुच्छ हो जाता है और फिर से लागू होता है। जब लोग एटिपिकल उपयोग के मामलों के बारे में सवाल पोस्ट करते हैं, तो वे अक्सर जानते हैं कि यह थोड़ा अजीब है, लेकिन खरोंच से इमारत बनाम कुछ विरासत में मिला हो सकता है, और शायद विवरण का खुलासा नहीं कर सकता।
मार्क बेनेट

4

आप एक और "ट्रिक" का उपयोग कर सकते हैं। उन पहचानकर्ताओं के केवल संग्रह को लोड करें जिसमें आप रुचि रखते हैं। मान लें कि पहचानकर्ता प्रकार लंबा = 8bytes है, तो 10 ^ 6 ऐसे पहचानकर्ताओं की एक सूची लगभग 8Mb बनाता है। यदि यह एक बैच प्रक्रिया है (एक समय में एक उदाहरण), तो यह सहने योग्य है। फिर बस इटरेट करें और काम करें।

एक और टिप्पणी - आपको वैसे भी इसे विखंडू में करना चाहिए - खासकर यदि आप रिकॉर्ड को संशोधित करते हैं, अन्यथा डेटाबेस में रोलबैक सेगमेंट बढ़ेगा।

जब यह पहले सेट करने की बात आती है / अधिकतम / अधिकतम रणनीति - यह बहुत ऊपर से परिणाम के लिए बहुत धीमी है।

यह भी ध्यान रखें कि डेटाबेस संभवतः कम्यूटेड आइसोलेशन रीड में संचालित हो रहा है , इसलिए फैंटम रीड लोड लोडर्स से बचने के लिए और फिर संस्थाओं को एक-एक करके लोड करें (या 10 बाय 10 या जो भी हो)।


हाय @Marcin, क्या आप या कोई भी इस chunked और id-first stepwise दृष्टिकोण को लागू करने वाले उदाहरण कोड के लिए एक लिंक प्रदान कर सकते हैं, अधिमानतः Java8 धाराओं का उपयोग करते हुए?
krevelen

2

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

यहां प्रदर्शन बचत महत्वपूर्ण है, संभवतः जेपीए / हाइबरनेट / अप्पेरवर भूमि में आप जितना कुछ कर सकते हैं उससे अधिक तीव्रता के आदेश, और आपके डेटाबेस सर्वर के पास बड़े परिणाम सेट को कुशलतापूर्वक संसाधित करने के लिए अपने स्वयं के सर्वर साइड कर्सर तंत्र की सबसे अधिक संभावना होगी। प्रदर्शन बचत डेटाबेस सर्वर से एप्लिकेशन सर्वर पर डेटा को शिप करने के लिए नहीं होती है, जहां आप डेटा को संसाधित करते हैं, और फिर इसे वापस शिप करते हैं।

संग्रहीत प्रक्रियाओं का उपयोग करने के लिए कुछ महत्वपूर्ण डाउनसाइड हैं जो आपके लिए पूरी तरह से इस पर शासन कर सकते हैं, लेकिन अगर आपको अपने व्यक्तिगत टूलबॉक्स में वह कौशल मिला है और इस तरह की स्थिति में इसका उपयोग कर सकते हैं, तो आप इन प्रकार की चीजों को काफी जल्दी से बाहर निकाल सकते हैं। ।


1
-2 डाउनवोट्स - क्या अगला डाउनवॉटर आपके डाउनवोट का बचाव करेगा?
डेंजर

1
मैंने इनको पढ़ते हुए भी यही बात सोची। सवाल यूआई के साथ एक उच्च मात्रा बैच नौकरी इंगित करता है। यह मानते हुए कि आपको ऐप सर्वर विशिष्ट संसाधनों की आवश्यकता नहीं है, ऐप सर्वर का उपयोग क्यों करें? संग्रहीत प्रक्रिया बहुत अधिक कुशल होगी।
jdessey

@jdessey स्थिति के आधार पर, मान लें कि हमारे पास एक आयात सुविधा है जहाँ आयात पर इसे सिस्टम के कुछ अन्य भाग के साथ कुछ करना चाहिए जैसे कुछ व्यावसायिक नियमों के आधार पर दूसरी तालिका में पंक्तियों को जोड़ना जो पहले से ही EJB के रूप में कोडित किए गए हैं। तब ऐप सर्वर में चलने से अधिक समझ में आता है, जब तक कि आप एम्बेडेड मोड में चलने के लिए ईजेबी प्राप्त नहीं कर सकते।
आर्किमिडीज ट्रैजानो सेप

1

@Tomasz Nurkiewicz के उत्तर पर विस्तार करने के लिए। आपके पास पहुंच है DataSourceजो बदले में आपको एक कनेक्शन प्रदान कर सकता है

@Resource(name = "myDataSource",
    lookup = "java:comp/DefaultDataSource")
private DataSource myDataSource;

आपके कोड में

try (Connection connection = myDataSource.getConnection()) {
    // raw jdbc operations
}

यह आपको आयात / निर्यात जैसे कुछ विशिष्ट बड़े बैच संचालनों के लिए जेपीए को बायपास करने की अनुमति देगा, हालांकि यदि आवश्यक हो तो आप अभी भी अन्य जेपीए संचालन के लिए इकाई प्रबंधक तक पहुंच सकते हैं।


0

Paginationपरिणाम प्राप्त करने के लिए अवधारणा का उपयोग करें


4
GUI के लिए Pagination बहुत अच्छा है। लेकिन भारी मात्रा में डेटा को संसाधित करने के लिए स्क्रॉलेबलबेल्टसेट का आविष्कार बहुत पहले किया गया था। यह सिर्फ जेपीए में नहीं है।
extraneon

0

मैंने खुद यह सोचा है। यह मामला लगता है:

  • आपका डेटासेट कितना बड़ा है (पंक्तियाँ)
  • जेपीए कार्यान्वयन आप क्या उपयोग कर रहे हैं
  • आप प्रत्येक पंक्ति के लिए किस प्रकार की प्रक्रिया कर रहे हैं।

मैंने एक Iterator लिखा है ताकि दोनों दृष्टिकोणों को ढूंढना आसान हो जाए (findAll vs findEntries)।

मैं आपको दोनों की कोशिश करने की सलाह देता हूं।

Long count = entityManager().createQuery("select count(o) from Model o", Long.class).getSingleResult();
ChunkIterator<Model> it1 = new ChunkIterator<Model>(count, 2) {

    @Override
    public Iterator<Model> getChunk(long index, long chunkSize) {
        //Do your setFirst and setMax here and return an iterator.
    }

};

Iterator<Model> it2 = List<Model> models = entityManager().createQuery("from Model m", Model.class).getResultList().iterator();


public static abstract class ChunkIterator<T> 
    extends AbstractIterator<T> implements Iterable<T>{
    private Iterator<T> chunk;
    private Long count;
    private long index = 0;
    private long chunkSize = 100;

    public ChunkIterator(Long count, long chunkSize) {
        super();
        this.count = count;
        this.chunkSize = chunkSize;
    }

    public abstract Iterator<T> getChunk(long index, long chunkSize);

    @Override
    public Iterator<T> iterator() {
        return this;
    }

    @Override
    protected T computeNext() {
        if (count == 0) return endOfData();
        if (chunk != null && chunk.hasNext() == false && index >= count) 
            return endOfData();
        if (chunk == null || chunk.hasNext() == false) {
            chunk = getChunk(index, chunkSize);
            index += chunkSize;
        }
        if (chunk == null || chunk.hasNext() == false) 
            return endOfData();
        return chunk.next();
    }

}

मैंने अपने चंक इटरेटर का उपयोग नहीं किया (इसलिए यह परीक्षण नहीं हो सकता है)। वैसे अगर आपको इसका इस्तेमाल करना है तो आपको google संग्रहों की आवश्यकता होगी।


"प्रत्येक पंक्ति के लिए आप किस प्रकार की प्रक्रिया कर रहे हैं" के बारे में - अगर # पंक्तियों की संख्या लाखों में है, तो मुझे संदेह है कि केवल एक आईडी कॉलम के साथ एक साधारण वस्तु भी समस्या पैदा करने वाली है। मैंने भी अपने स्वयं के Iterator को लिखने के बारे में सोचा था जो setFirstResult / setMaxResult को लपेटते थे, लेकिन मुझे लगा कि यह एक आम (और उम्मीद है कि हल!) मुद्दा होना चाहिए।
बजे जॉर्ज आर्महोल्ड

@ कैफीन कोमा मैंने अपना Iterator पोस्ट किया आप शायद कुछ और JPA कर सकते हैं। मुझे बताओ अगर यह मदद करता है। मैं (नहीं एक खोज) का उपयोग कर समाप्त हो गया।
एडम गेन

0

हाइबरनेट के साथ 4 अलग-अलग तरीके हैं जो आप चाहते हैं। प्रत्येक में डिज़ाइन ट्रेडऑफ़्स, सीमाएँ और परिणाम हैं। मेरा सुझाव है कि प्रत्येक की खोज और निर्णय करना जो आपकी स्थिति के लिए सही है।

  1. स्क्रॉल के साथ स्टेटलेस सत्र का उपयोग करें ()
  2. प्रत्येक पुनरावृत्ति के बाद session.clear () का उपयोग करें। जब अन्य संस्थाओं को संलग्न करने की आवश्यकता होती है, तो उन्हें एक अलग सत्र में लोड करें। प्रभावी रूप से पहला सत्र स्टेटलेस सत्र का अनुकरण कर रहा है, लेकिन जब तक वस्तुओं को अलग नहीं किया जाता, तब तक एक राज्य सत्र की सभी विशेषताओं को बनाए रखना।
  3. Iterate () या सूची () का उपयोग करें, लेकिन पहले क्वेरी में केवल id प्राप्त करें, फिर प्रत्येक पुनरावृत्ति में एक अलग सत्र में, सत्र करें। सत्र को लोड करें और पुनरावृत्ति के अंत में सत्र बंद करें।
  4. EntityManager.detach () aka Session.evict () के साथ Query.iterate () का उपयोग करें;

0

यहां एक सरल, सीधा जेपीए उदाहरण (कोटलिन में) दिखाया गया है कि आप एक कर्सर का उपयोग किए बिना, एक समय में 100 आइटमों को पढ़ते हुए, एक मनमाने ढंग से बड़े परिणाम सेट पर कैसे पैगेट कर सकते हैं (प्रत्येक कर्सर डेटाबेस पर संसाधनों का उपभोग करता है)। यह कीसेट पेजिंग का उपयोग करता है।

देखें https://use-the-index-luke.com/no-offset कीसेट पृष्ठांकन की अवधारणा के लिए, और https://www.citusdata.com/blog/2016/03/30/five-ways-to- पेजेट / अपनी कमियों के साथ अलग-अलग तरीकों की तुलना के लिए।

/*
create table my_table(
  id int primary key, -- index will be created
  my_column varchar
)
*/

fun keysetPaginationExample() {
    var lastId = Integer.MIN_VALUE
    do {

        val someItems =
        myRepository.findTop100ByMyTableIdAfterOrderByMyTableId(lastId)

        if (someItems.isEmpty()) break

        lastId = someItems.last().myTableId

        for (item in someItems) {
          process(item)
        }

    } while (true)
}

0

जेपीए और नेटिविक के साथ एक उदाहरण हर बार साइज़ एलीमेंट्स ऑफ़सेट्स का उपयोग करता है

public List<X> getXByFetching(int fetchSize) {
        int totalX = getTotalRows(Entity);
        List<X> result = new ArrayList<>();
        for (int offset = 0; offset < totalX; offset = offset + fetchSize) {
            EntityManager entityManager = getEntityManager();
            String sql = getSqlSelect(Entity) + " OFFSET " + offset + " ROWS";
            Query query = entityManager.createNativeQuery(sql, X.class);
            query.setMaxResults(fetchSize);
            result.addAll(query.getResultList());
            entityManager.flush();
            entityManager.clear();
        return result;
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.