java - एक वनू-रिलेशन आलसी बनाना




hibernate jpa (4)

इस एप्लिकेशन में हम विकास कर रहे हैं, हमने देखा है कि एक दृश्य विशेष रूप से धीमा था। मैंने दृश्य को प्रोफाइल किया और देखा कि हाइबरनेट द्वारा निष्पादित एक प्रश्न था जिसमें 10 सेकेंड लग गए थे, भले ही डेटाबेस में केवल दो ऑब्जेक्ट प्राप्त हों। सभी OneToMany और कई बहुत से संबंध आलसी थे ताकि समस्या नहीं थी। वास्तविक एसक्यूएल निष्पादित किए जाने का निरीक्षण करते समय, मैंने देखा कि क्वेरी में 80 से अधिक शामिल थे।

इस मुद्दे का और निरीक्षण करते हुए, मैंने देखा कि समस्या इकाई वर्गों के बीच OneToOne और ManyToOne संबंधों के गहरे पदानुक्रम के कारण हुई थी। तो, मैंने सोचा, मैं उन्हें आलसी लाऊंगा, जिससे समस्या हल होनी चाहिए। लेकिन @OneToOne(fetch=FetchType.LAZY) या @OneToOne(fetch=FetchType.LAZY) करने का काम नहीं लगता है। या तो मुझे अपवाद मिलता है या फिर वे वास्तव में प्रॉक्सी ऑब्जेक्ट के साथ प्रतिस्थापित नहीं होते हैं और इस प्रकार आलसी होते हैं।

कोई विचार है कि मैं इसे काम करने के लिए कैसे प्राप्त करूं? ध्यान दें कि मैं संबंधों या कॉन्फ़िगरेशन विवरण को परिभाषित करने के लिए persistence.xml का उपयोग नहीं करता, सबकुछ जावा कोड में किया जाता है।


आलसी लोडिंग को एक-से-एक मैपिंग पर काम करने के लिए आपको हाइबरनेट को समय-समय पर संकलन करने की आवश्यकता है और एक-से-एक संबंध में @LazyToOne(value = LazyToOneOption.NO_PROXY) जोड़ें।

उदाहरण मैपिंग:

@OneToOne(fetch = FetchType.LAZY)  
@JoinColumn(name="other_entity_fk")
@LazyToOne(value = LazyToOneOption.NO_PROXY)
public OtherEntity getOther()

उदाहरण चींटी बिल्ड फ़ाइल एक्सटेंशन (हाइबरनेट संकलन समय उपकरण करने के लिए):

<property name="src" value="/your/src/directory"/><!-- path of the source files --> 
<property name="libs" value="/your/libs/directory"/><!-- path of your libraries --> 
<property name="destination" value="/your/build/directory"/><!-- path of your build directory --> 

<fileset id="applibs" dir="${libs}"> 
  <include name="hibernate3.jar" /> 
  <!-- include any other libraries you'll need here --> 
</fileset> 

<target name="compile"> 
  <javac srcdir="${src}" destdir="${destination}" debug="yes"> 
    <classpath> 
      <fileset refid="applibs"/> 
    </classpath> 
  </javac> 
</target> 

<target name="instrument" depends="compile"> 
  <taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask"> 
    <classpath> 
      <fileset refid="applibs"/> 
    </classpath> 
  </taskdef> 

  <instrument verbose="true"> 
    <fileset dir="${destination}"> 
      <!-- substitute the package where you keep your domain objs --> 
      <include name="/com/mycompany/domainobjects/*.class"/> 
    </fileset> 
  </instrument> 
</target>

जैसा कि पहले से ही पूरी तरह से ChssPly76 द्वारा समझाया गया है, हाइबरनेट के प्रॉक्सी अनियंत्रित (शून्य) एक-से-एक संगठनों में मदद नहीं करते हैं, लेकिन उपकरण स्थापित करने से बचने के लिए here एक चाल है। विचार है कि हाइबरनेट को बेवकूफ बनाना कि इकाई वर्ग जिसे हम उपयोग करना चाहते हैं, पहले से ही वाद्य यंत्रित किया गया है: आप इसे स्रोत कोड में मैन्युअल रूप से वाद्य यंत्र बनाते हैं। यह आसान है! मैंने इसे CGLib के साथ बाइटकोड प्रदाता के रूप में कार्यान्वित किया है और यह काम करता है (सुनिश्चित करें कि आप आलसी = "नो-प्रॉक्सी" कॉन्फ़िगर करें और fetch = "select", "एचबीएम में" शामिल न हों)।

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


यहां कुछ ऐसा है जो मेरे लिए काम कर रहा है (उपकरण के बिना):

दोनों तरफ @OneToOne का उपयोग करने के बजाय, मैं रिश्ते के विपरीत भाग में @OneToOne का उपयोग करता @OneToOne ( mappedBy )। इससे संपत्ति एक संग्रह (नीचे दिए गए उदाहरण में List ) बनाती है, लेकिन मैं इसे गेटटर में एक आइटम में अनुवाद करता हूं, जिससे ग्राहकों को पारदर्शी बना दिया जाता है।

यह सेटअप आलसी काम करता है, यानी, चयन केवल तभी किए जाते हैं जब getPrevious() या getNext() को कॉल किया जाता है - और प्रत्येक कॉल के लिए केवल एक ही चयन किया जाता है।

टेबल संरचना:

CREATE TABLE `TB_ISSUE` (
    `ID`            INT(9) NOT NULL AUTO_INCREMENT,
    `NAME`          VARCHAR(255) NULL,
    `PREVIOUS`      DECIMAL(9,2) NULL
    CONSTRAINT `PK_ISSUE` PRIMARY KEY (`ID`)
);
ALTER TABLE `TB_ISSUE` ADD CONSTRAINT `FK_ISSUE_ISSUE_PREVIOUS`
                 FOREIGN KEY (`PREVIOUS`) REFERENCES `TB_ISSUE` (`ID`);

कक्षा:

@Entity
@Table(name = "TB_ISSUE") 
public class Issue {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Integer id;

    @Column
    private String name;

    @OneToOne(fetch=FetchType.LAZY)  // one to one, as expected
    @JoinColumn(name="previous")
    private Issue previous;

    // use @OneToMany instead of @OneToOne to "fake" the lazy loading
    @OneToMany(mappedBy="previous", fetch=FetchType.LAZY)
    // notice the type isnt Issue, but a collection (that will have 0 or 1 items)
    private List<Issue> next;

    public Integer getId() { return id; }
    public String getName() { return name; }

    public Issue getPrevious() { return previous; }
    // in the getter, transform the collection into an Issue for the clients
    public Issue getNext() { return next.isEmpty() ? null : next.get(0); }

}

सबसे पहले, केएलई के जवाब में कुछ स्पष्टीकरण:

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

  2. कई से एक संगठन (और एक से कई, जाहिर है) इस मुद्दे से पीड़ित नहीं हैं। मालिक इकाई आसानी से अपने स्वयं के एफके की जांच कर सकती है (और एक से अधिक के मामले में, खाली संग्रह प्रॉक्सी शुरू में और मांग पर आबादी बनाई जाती है), इसलिए एसोसिएशन आलसी हो सकता है।

  3. एक-से-एक के साथ एक-दूसरे को प्रतिस्थापित करना काफी अच्छा विचार नहीं है। आप इसे अनूठे के साथ बदल सकते हैं लेकिन अन्य (संभवतः बेहतर) विकल्प हैं।

रॉब एच का एक वैध बिंदु है, हालांकि आप अपने मॉडल के आधार पर इसे लागू करने में सक्षम नहीं हो सकते हैं (उदाहरण के लिए यदि आपका एक-से-एक संगठन शून्य है)।

अब तक, जहां तक ​​मूल प्रश्न जाता है:

ए) @ManyToOne(fetch=FetchType.LAZY) बस ठीक काम करना चाहिए। क्या आप वाकई क्वेरी में ओवरराइट नहीं हो रहे हैं? एचक्यूएल में join fetch में join fetch संभव है और / या स्पष्ट रूप से मानदंड एपीआई के माध्यम से fetch मोड सेट करें जो क्लास एनोटेशन पर प्राथमिकता लेगा। यदि यह मामला नहीं है और आपको अभी भी समस्याएं आ रही हैं, तो कृपया अपनी कक्षाएं, क्वेरी और परिणामी एसक्यूएल को अधिक टू-द-पॉइंट वार्तालाप के लिए पोस्ट करें।

बी) @OneToOne trickier है। यदि यह निश्चित रूप से शून्य नहीं है, तो रॉब एच के सुझाव के साथ जाएं और इसे इस प्रकार निर्दिष्ट करें:

@OneToOne(optional = false, fetch = FetchType.LAZY)

अन्यथा, यदि आप अपना डेटाबेस बदल सकते हैं (मालिक तालिका में एक विदेशी कुंजी कॉलम जोड़ें), ऐसा करें और इसे "शामिल" के रूप में मैप करें:

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name="other_entity_fk")
public OtherEntity getOther()

और अन्य ईमानदारी में:

@OneToOne(mappedBy = "other")
public OwnerEntity getOwner()

यदि आप ऐसा नहीं कर सकते (और उत्सुक fetching के साथ नहीं रह सकते हैं) बाइटकोड वाद्ययंत्र आपका एकमात्र विकल्प है। मुझे सीपीरकिन्स से सहमत होना है, हालांकि - अगर आपके पास 80 है !!! उत्सुक OneToOne संघों के कारण जुड़ता है, तो आपको बड़ी समस्याएं मिलती हैं तो :-)