जेपीए उत्सुकता में शामिल नहीं होता है


112

जेपीए की भ्रूण रणनीति क्या वास्तव में नियंत्रित करती है? मैं उत्सुक और आलसी के बीच किसी भी अंतर का पता नहीं लगा सकता। दोनों मामलों में जेपीए / हाइबरनेट स्वचालित रूप से कई-से-एक रिश्तों में शामिल नहीं होता है।

उदाहरण: व्यक्ति का एक ही पता होता है। एक पता कई लोगों का हो सकता है। JPA एनोटेट निकाय वर्ग इस तरह दिखते हैं:

@Entity
public class Person {
    @Id
    public Integer id;

    public String name;

    @ManyToOne(fetch=FetchType.LAZY or EAGER)
    public Address address;
}

@Entity
public class Address {
    @Id
    public Integer id;

    public String name;
}

अगर मैं JPA क्वेरी का उपयोग करता हूं:

select p from Person p where ...

JPA / हाइबरनेट व्यक्ति तालिका से चयन करने के लिए एक SQL क्वेरी उत्पन्न करता है, और फिर प्रत्येक व्यक्ति के लिए एक अलग पता क्वेरी :

select ... from Person where ...
select ... from Address where id=1
select ... from Address where id=2
select ... from Address where id=3

बड़े परिणाम सेट के लिए यह बहुत बुरा है। यदि 1000 लोग हैं तो यह 1001 प्रश्न उत्पन्न करता है (1 व्यक्ति से और 1000 पता से अलग)। मुझे यह पता है क्योंकि मैं MySQL के क्वेरी लॉग को देख रहा हूं। यह मेरी समझ थी कि पते के भ्रूण प्रकार को उत्सुकता के साथ सेट करने से जेपीए / हाइबरनेट स्वतः जुड़ जाएगा। हालाँकि, फ़ेच प्रकार की परवाह किए बिना, यह अभी भी रिश्तों के लिए अलग-अलग प्रश्न उत्पन्न करता है।

केवल जब मैं स्पष्ट रूप से इसमें शामिल होने के लिए कहता हूं तो क्या यह वास्तव में शामिल होता है:

select p, a from Person p left join p.address a where ...

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

संपादित करें: यह प्रतीत होता है (हाइबरनेट FAQ यहां और यहां देखें ) जो FetchTypeजेपीए प्रश्नों को प्रभावित नहीं करता है। इसलिए मेरे मामले में मैंने स्पष्ट रूप से इसमें शामिल होने के लिए कहा है।


5
एफएक्यू प्रविष्टियों के लिंक टूटे हुए हैं, यहां एक काम कर रहा है
n0weak

जवाबों:


99

जेपीए भ्रूण की रणनीति का चयन करने के लिए मानचित्रण एनोटेशन पर कोई विनिर्देश प्रदान नहीं करता है। सामान्य तौर पर, संबंधित संस्थाओं को नीचे दिए गए तरीकों में से किसी एक में लाया जा सकता है

  • चुनें => रूट एंटिटीज के लिए एक क्वेरी + संबंधित मैप्ड इकाई के लिए एक क्वेरी / प्रत्येक रूट एंटिटी का संग्रह = (n + 1) प्रश्न
  • सदस्यता => मूल संस्थाओं के लिए एक क्वेरी + संबंधित मैप की गई इकाई के लिए दूसरी क्वेरी / प्रथम श्रेणी = सभी प्रश्नों में प्राप्त सभी रूट संस्थाओं का संग्रह
  • JOIN => एक क्वेरी दोनों रूट एंटिटीज़ और उनके सभी मैप्ड एंटिटी / कलेक्शन = 1 क्वेरी को लाने के लिए

तो SELECTऔर JOINदो चरम सीमाएं हैं और SUBSELECTबीच में पड़ती हैं। व्यक्ति अपने डोमेन मॉडल के आधार पर उपयुक्त रणनीति चुन सकता है।

डिफ़ॉल्ट रूप SELECTसे JPA / EclipseLink और Hibernate दोनों द्वारा उपयोग किया जाता है। इसका उपयोग करके ओवरराइड किया जा सकता है:

@Fetch(FetchMode.JOIN) 
@Fetch(FetchMode.SUBSELECT)

हाइबरनेट में। यह SELECTमोड को स्पष्ट रूप से उपयोग करने की अनुमति देता है, @Fetch(FetchMode.SELECT)जिसे बैच आकार जैसे का उपयोग करके ट्यून किया जा सकता है@BatchSize(size=10)

EclipseLink में संगत एनोटेशन हैं:

@JoinFetch
@BatchFetch

5
उन सेटिंग्स क्यों मौजूद है? मुझे लगता है कि JOIN को लगभग हमेशा इस्तेमाल किया जाना चाहिए। अब मुझे हाइबरनेट-विशिष्ट एनोटेशन के साथ सभी मैपिंग को चिह्नित करना होगा।
vbzhenar

4
दिलचस्प है लेकिन दुख की बात है @Fetch (FetchMode.JOIN) मेरे लिए (हाइबरनेट 4.2.15), जेपीक्यूएल में मानदंड के रूप में बिल्कुल भी काम नहीं करता है।
Apxx

3
हाइबरनेट एनोटेशन मेरे लिए बिल्कुल भी काम नहीं करता है, स्प्रिंग जेपीए का उपयोग करते हुए
TheBakker

2
@Aphax यह हो सकता है क्योंकि हाइबरनेट JPAQL / मानदंड बनाम em.find () के लिए विभिन्न डिफ़ॉल्ट रणनीतियों का उपयोग करता है। Vladmihalcea.com/2013/10/17/… और संदर्भ प्रलेखन देखें ।
जोशुआ डेविस

1
@vbezhenar (और कुछ समय बाद उसकी टिप्पणी पढ़ते हुए): डेटाबेस में कार्टिजियन उत्पाद उत्पन्न करने के लिए तैयार करें ताकि आप सुनिश्चित करें कि आप चाहते हैं कि कार्टेशियन उत्पाद की गणना की जाए। ध्यान दें कि यदि आप लाँच जॉइन का उपयोग करते हैं, भले ही आप LAZY डालते हैं, तो यह उत्सुक रूप से भरा हुआ होगा।
वालफ्रैट

45

"mxc" सही है। fetchTypeकेवल तब निर्दिष्ट करता है जब संबंध को हल किया जाना चाहिए।

बाहरी जोड़ का उपयोग करके उत्सुक लोडिंग का अनुकूलन करने के लिए आपको जोड़ना होगा

@Fetch(FetchMode.JOIN)

अपने क्षेत्र के लिए। यह हाइबरनेट विशिष्ट एनोटेशन है।


6
यह मेरे लिए हाइबरनेट 4.2.15 के साथ JPQL या मानदंड में काम नहीं करता है।
Apxx

6
@ Aphax मुझे लगता है कि क्योंकि JPAQL और मानदंड लेश विनिर्देश का पालन नहीं करते हैं। Fetch एनोटेशन केवल em.find (), AFAIK के लिए काम करता है। देखें vladmihalcea.com/2013/10/17/… इसके अलावा, हाइबरनेट डॉक्टर देखें । मुझे पूरा यकीन है कि यह कहीं न कहीं कवर है।
जोशुआ डेविस

@JoshuaDavis मेरा क्या मतलब है कि \ _Fetch एनोटेशन किसी भी तरह के JOIN ऑप्टिमाइज़ेशन को प्रश्नों, wheter JPQL या em.find () में लागू नहीं करता है, मुझे सिर्फ Hibernate 5.2 पर एक और कोशिश करनी थी। + और यह अभी भी वही है
Aphax

38

FetchType विशेषता नियंत्रित करती है कि एनोटेट फ़ील्ड को तुरंत प्राप्त किया जाता है जब प्राथमिक इकाई को लाया जाता है। यह जरूरी नहीं है कि कैसे कथनों का निर्माण किया जाए, वास्तविक sql कार्यान्वयन उस प्रदाता पर निर्भर करता है जिसे आप toplink / hibernate आदि का उपयोग कर रहे हैं।

यदि आप सेट करते हैं, तो fetchType=EAGERइसका मतलब है कि एनोटेट फ़ील्ड इकाई में अन्य फ़ील्ड्स के समान ही अपने मानों के साथ पॉपुलेटेड है। इसलिए यदि आप एक इकाई प्रबंधक खोलते हैं, तो अपने व्यक्ति की वस्तुओं को पुनः प्राप्त करें और फिर इकाई प्रबंधक को बंद कर दें, बाद में एक व्यक्ति कर रहा है। इसके परिणामस्वरूप एक आलसी लोड अपवाद नहीं होगा।

यदि आप सेट करते हैं fetchType=LAZY करते हैं तो फ़ील्ड एक्सेस होने पर ही पॉपुलेटेड होती है। यदि आपने शवदाह गृह को बंद कर दिया है, तो एक आलसी लोड अपवाद को फेंक दिया जाएगा यदि आप एक व्यक्ति करते हैं। फ़ील्ड को लोड करने के लिए आपको इकाई को एक इकाई संचालक संदर्भ में em.merge () के साथ वापस लाने की आवश्यकता है, फिर फ़ील्ड एक्सेस करें और फिर एंटोमेनजर को बंद करें।

ग्राहक आदेशों के संग्रह के साथ ग्राहक वर्ग का निर्माण करते समय आप आलसी लोडिंग चाहते हो सकते हैं। यदि आप एक ग्राहक के लिए हर आदेश को पुनः प्राप्त करते हैं जब आप एक ग्राहक सूची प्राप्त करना चाहते थे तो यह एक महंगा डेटाबेस ऑपरेशन हो सकता है जब आप केवल ग्राहक के नाम और संपर्क विवरण की तलाश कर रहे हों। बाद में db तक पहुँच को छोड़ना सबसे अच्छा है।

प्रश्न के दूसरे भाग के लिए - अनुकूलित SQL उत्पन्न करने के लिए हाइबरनेट कैसे प्राप्त करें?

हाइबरनेट आपको संकेत प्रदान करने की अनुमति देना चाहिए कि कैसे सबसे कुशल क्वेरी का निर्माण किया जाए लेकिन मुझे संदेह है कि आपके टेबल निर्माण में कुछ गड़बड़ है। क्या संबंध तालिकाओं में स्थापित है? हाइबरनेट ने निर्णय लिया हो सकता है कि एक साधारण क्वेरी सम्मिलित होने की तुलना में जल्दी होगी, खासकर यदि अनुक्रमणिका आदि गायब हैं।


20

इसके साथ प्रयास करें:

select p from Person p left join FETCH p.address a where...

यह मेरे लिए JPA2 / EclipseLink के समान काम करता है, लेकिन ऐसा लगता है कि यह सुविधा JPA1 में भी मौजूद है :


7

यदि आप हाइबरनेट के बजाय EclipseLink का उपयोग करते हैं तो आप "क्वेरी संकेत" द्वारा अपने प्रश्नों को अनुकूलित कर सकते हैं। ग्रहण विकी: एक्लिप्सलिंक / उदाहरण / JPA / QueryOptimization के इस लेख को देखें ।

"ज्वाइन रीडिंग" के बारे में एक अध्याय है।


2

जुड़ने के लिए आप कई काम कर सकते हैं (eclipselink का उपयोग करके)

  • jpql में आप लेफ्ट जॉइन कर सकते हैं

  • नामित क्वेरी में आप क्वेरी संकेत निर्दिष्ट कर सकते हैं

  • TypedQuery में आप कुछ कह सकते हैं

    query.setHint("eclipselink.join-fetch", "e.projects.milestones");

  • वहाँ भी बैच संकेत है

    query.setHint("eclipselink.batch", "e.address");

देख

http://java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html


1

मुझे अपवाद के साथ यह समस्या थी कि व्यक्ति वर्ग में एक एम्बेडेड कुंजी वर्ग था। मेरा खुद का समाधान उन्हें क्वेरी में शामिल करना और निकालना था

@Fetch(FetchMode.JOIN)

मेरा एम्बेडेड आईडी वर्ग:

@Embeddable
public class MessageRecipientId implements Serializable {

    @ManyToOne(targetEntity = Message.class, fetch = FetchType.LAZY)
    @JoinColumn(name="messageId")
    private Message message;
    private String governmentId;

    public MessageRecipientId() {
    }

    public Message getMessage() {
        return message;
    }

    public void setMessage(Message message) {
        this.message = message;
    }

    public String getGovernmentId() {
        return governmentId;
    }

    public void setGovernmentId(String governmentId) {
        this.governmentId = governmentId;
    }

    public MessageRecipientId(Message message, GovernmentId governmentId) {
        this.message = message;
        this.governmentId = governmentId.getValue();
    }

}

-1

मुझे दो चीजें होती हैं।

पहले, क्या आप सुनिश्चित हैं कि आप पता के लिए ManyToOne का मतलब है? इसका मतलब है कि कई लोगों का पता एक ही होगा। यदि यह उनमें से किसी एक के लिए संपादित किया गया है, तो यह उन सभी के लिए संपादित किया जाएगा। क्या आपका इरादा ऐसा है? 99% समय के पते "निजी" हैं (इस अर्थ में कि वे केवल एक व्यक्ति के हैं)।

दूसरे, क्या आपके पास पर्सन इकाई पर कोई अन्य उत्सुक संबंध हैं? यदि मुझे सही ढंग से याद है, तो हाइबरनेट केवल एक इकाई पर एक उत्सुक संबंध को संभाल सकता है लेकिन संभवतः पुरानी जानकारी है।

मैं कहता हूं कि क्योंकि आपकी समझ यह है कि यह कैसे काम करना चाहिए, यह अनिवार्य रूप से सही है जहां मैं बैठा हूं।


यह एक बना हुआ उदाहरण है जो कई-से-एक का उपयोग करता है। व्यक्ति-पता सबसे अच्छा उदाहरण नहीं हो सकता है। मुझे अपने कोड में कोई अन्य उत्सुक प्रकार दिखाई नहीं दे रहा है।
स्टीव कूओ

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

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