JPA के साथ EntityManager.find () बनाम EntityManager.getReference () का उपयोग कब करें


103

मैं एक स्थिति में आया हूं (जो मुझे लगता है कि अजीब है, लेकिन संभवतः काफी सामान्य है) जहां मैं एक डेटाबेस इकाई प्राप्त करने के लिए EntityManager.getReference (LObj.getClass (), LObj.getId ()) का उपयोग करता हूं और फिर लौटी हुई वस्तु को पास करता हूं दूसरी तालिका में बने रहें।

तो मूल रूप से प्रवाह इस तरह था:

वर्ग TFacade {

  createT (फोब्ज, एओबीजे) {
    टी टुबज = नया टी ();
    TObj.setF (FObj);
    TObj.setA (AObj);
    ...
    EntityManager.persist (TObj);
    ...
    एल LOjj = A.getL ();
    FObj.setL (LObj);
    FFacade.editF (FObj);
  }
}

@ TransactionAttributeType.REQUIRES_NEW
कक्षा FFacade {

  editF (FObj) {
    एल LOjj = FObj.getL ();
    LObj = EntityManager.getReference (LObj.getClass (), LObj.getId ());
    ...
    EntityManager.merge (FObj);
    ...
    FLHFacade.create (FObj, LObj);
  }
}

@ TransactionAttributeType.REQUIRED
कक्षा FLHFacade {

  createFLH (FObj, LObj) {
    FLH FLHObj = नया FLH ();
    FLHObj.setF (FObj);
    FLHObj.setL (LObj);
    ....
    EntityManager.persist (FLHObj);
    ...
  }
}

मुझे निम्न अपवाद मिल रहा था "java.lang.IllegalArgumentException: अज्ञात इकाई: com.my.persistence.L $$ एन्हांसरबिजली $ $ 3e7987d0"

थोड़ी देर तक इसे देखने के बाद, मुझे अंततः पता चला कि ऐसा इसलिए था क्योंकि मैं EntityManager.getReference () विधि का उपयोग कर रहा था, क्योंकि मुझे उपरोक्त अपवाद मिल रहा था क्योंकि विधि एक प्रॉक्सी लौटा रही थी।

यह मुझे आश्चर्यचकित करता है, जब EntityManager.getReference () विधि का उपयोग EntityManager.find () विधि के बजाय करना उचित है ?

EntityManager.getReference () एक EntityNotFoundException को फेंकता है यदि यह उस इकाई को नहीं खोज सकता है जिसके लिए खोज की जा रही है जो अपने आप में बहुत सुविधाजनक है। EntityManager.find () विधि केवल शून्य देता है यदि वह इकाई नहीं पाता है।

लेन-देन की सीमाओं के संबंध में, मुझे लगता है कि आपको नए पाया जाने वाले निकाय को नए लेन-देन से पहले खोजने () विधि का उपयोग करने की आवश्यकता होगी। यदि आप getReference () विधि का उपयोग करते हैं तो आप संभवतः उपरोक्त अपवाद के साथ खदान के समान स्थिति में समाप्त हो जाएंगे।


उल्लेख करना भूल गया, मैं JPA प्रदाता के रूप में हाइबरनेट का उपयोग कर रहा हूं।
सिब्ज़ टीचर

जवाबों:


152

जब मैं डेटाबेस स्थिति तक पहुँचने की आवश्यकता नहीं होती है, तो मैं आमतौर पर गेट रीफरेंस विधि का उपयोग करता हूं (मेरा मतलब है कि गेटर विधि)। बस राज्य बदलने के लिए (मेरा मतलब है सेटर विधि)। जैसा कि आपको पता होना चाहिए, getReference एक प्रॉक्सी ऑब्जेक्ट देता है जो एक शक्तिशाली सुविधा का उपयोग करता है जिसे स्वचालित गंदा जाँच कहा जाता है। मान लीजिए निम्नलिखित

public class Person {

    private String name;
    private Integer age;

}


public class PersonServiceImpl implements PersonService {

    public void changeAge(Integer personId, Integer newAge) {
        Person person = em.getReference(Person.class, personId);

        // person is a proxy
        person.setAge(newAge);
    }

}

तो मैं फोन खोजने के विधि, जेपीए प्रदाता, पर्दे के पीछे, कॉल करेंगे

SELECT NAME, AGE FROM PERSON WHERE PERSON_ID = ?

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?

अगर मैं getReference विधि कहता हूं , पर्दे के पीछे JPA प्रदाता, कॉल करेगा

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?

और आपको पता है क्यों ???

जब आप getReference को कॉल करते हैं, तो आपको एक प्रॉक्सी ऑब्जेक्ट मिलेगा। कुछ इस तरह (जेपीए प्रदाता इस प्रॉक्सी को लागू करने का ख्याल रखता है)

public class PersonProxy {

    // JPA provider sets up this field when you call getReference
    private Integer personId;

    private String query = "UPDATE PERSON SET ";

    private boolean stateChanged = false;

    public void setAge(Integer newAge) {
        stateChanged = true;

        query += query + "AGE = " + newAge;
    }

}

इसलिए लेन-देन करने से पहले, JPA प्रदाता को अपडेट करने के लिए या व्यक्ति इकाई नहीं होने के लिए राज्य-ध्वजांकित ध्वज दिखाई देगा। यदि अपडेट स्टेटमेंट के बाद कोई पंक्तियाँ नहीं अपडेट की जाती हैं, तो JPA प्रदाता EntityNotFoundException को JPA विनिर्देश के अनुसार फेंक देगा।

सादर,


4
मैं EclipseLink 2.5.0 का उपयोग कर रहा हूं और ऊपर बताए गए प्रश्न सही नहीं हैं। यह हमेशा एक SELECTपहले जारी करता है UPDATE, कोई फर्क नहीं पड़ता find()/ जो getReference()मैं का उपयोग करें। इससे भी बदतर, SELECTNON-LAZY संबंधों (नया जारी करना SELECTS) का पता लगाता है , हालांकि मैं सिर्फ एक इकाई में एक ही क्षेत्र को अपडेट करना चाहता हूं।
देवान्न मिलोसेविक

1
@ आर्थर रोनाल्ड अगर getReference द्वारा इकाई में एक संस्करण एनोटेशन होता है तो क्या होता है?
डेविड हॉफमैन

मेरे पास @DejanMilosevic जैसा ही मुद्दा है: GetReference () के माध्यम से प्राप्त इकाई को हटाते समय, उस इकाई पर एक चयन जारी किया जाता है और यह उस इकाई के सभी LAZY संबंधों का पता लगाता है, इस प्रकार अन्य SELECTS (EclipseLink 2.5.0 के साथ) जारी करता है।
स्टीफन एपेरसेल

27

जैसा कि मैंने इस लेख में बताया है , मान लें कि आपके पास एक मूल Postसंस्था है और एक बच्चे PostCommentको निम्न चित्र में चित्रित किया गया है:

यहां छवि विवरण दर्ज करें

यदि आप एसोसिएशन findको सेट करने का प्रयास करते हैं, तो आप कॉल करते हैं @ManyToOne post:

PostComment comment = new PostComment();
comment.setReview("Just awesome!");

Post post = entityManager.find(Post.class, 1L);
comment.setPost(post);

entityManager.persist(comment);

हाइबरनेट निम्नलिखित कथनों को निष्पादित करेगा:

SELECT p.id AS id1_0_0_,
       p.title AS title2_0_0_
FROM   post p
WHERE p.id = 1

INSERT INTO post_comment (post_id, review, id)
VALUES (1, 'Just awesome!', 1)

चयन क्वेरी इस बार बेकार है क्योंकि हमें पोस्ट निकाय की आवश्यकता नहीं है। हम केवल अंतर्निहित post_id विदेशी कुंजी कॉलम सेट करना चाहते हैं।

अब, यदि आप getReferenceइसके बजाय उपयोग करते हैं:

PostComment comment = new PostComment();
comment.setReview("Just awesome!");

Post post = entityManager.getReference(Post.class, 1L);
comment.setPost(post);

entityManager.persist(comment);

इस बार, हाइबरनेट सिर्फ INSERT बयान जारी करेगा:

INSERT INTO post_comment (post_id, review, id)
VALUES (1, 'Just awesome!', 1)

इसके विपरीत find, getReferenceकेवल एक इकाई प्रॉक्सी लौटाता है जिसमें केवल पहचानकर्ता सेट होता है। यदि आप प्रॉक्सी तक पहुँचते हैं, तो संबद्ध SQL स्टेटमेंट तब तक चालू रहेगा जब तक EntityManager अभी भी खुला है।

हालाँकि, इस मामले में, हमें इकाई प्रॉक्सी तक पहुँचने की आवश्यकता नहीं है। हम केवल अंतर्निहित तालिका रिकॉर्ड के लिए विदेशी कुंजी का प्रचार करना चाहते हैं, इसलिए इस उपयोग के मामले में प्रॉक्सी लोड करना पर्याप्त है।

प्रॉक्सी लोड करते समय, आपको अवगत होना चाहिए कि यदि आप EntityManager के बंद होने के बाद प्रॉक्सी संदर्भ को एक्सेस करने का प्रयास करते हैं तो एक LazyInitializationException को फेंक दिया जा सकता है। से निपटने के बारे में अधिक जानकारी के लिए LazyInitializationException, इस लेख को देखें


1
यह जानने के लिए हमें धन्यवाद देने के लिए व्लाद! लेकिन javadoc के अनुसार यह परेशान करने वाला लगता है: "जब गेटरेंस कहा जाता है, तो दृढ़ता प्रदाता रनटाइम को EntityNotFoundException को फेंकने की अनुमति है"। यह एक SELECT (पंक्ति अस्तित्व की जाँच के लिए कम से कम) के बिना संभव नहीं है, क्या यह है? इसलिए अंततः चयन कार्यान्वयन पर निर्भर करता है।
adrhc

3
आपके द्वारा वर्णित उपयोग के मामले के लिए, हाइबरनेट hibernate.jpa.compliance.proxyकॉन्फ़िगरेशन संपत्ति प्रदान करता है , इसलिए आप या तो जेपीए अनुपालन या बेहतर डेटा एक्सेस प्रदर्शन चुन सकते हैं।
व्लाद मिहालसीया

@VladMihalcea getReferenceकी ज़रूरत क्यों है अगर यह पीके सेट के साथ मॉडल के नए उदाहरण सेट करने के लिए पर्याप्त है। मैं क्या खो रहा हूँ?
rilaby

यह केवल हाइबरनेरी में समर्थित है यदि आप ट्रैवर्स किए जाने पर एसोसिएशन को लोड करने की अनुमति नहीं देंगे।
व्लाद मिहालसीया

8

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

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

MyLargeObject myObject = em.getReference(MyLargeObject.class, objectId);
em.remove(myObject);

7

यह मुझे आश्चर्यचकित करता है, जब EntityManager.getReference () विधि का उपयोग EntityManager.find () विधि के बजाय करना उचित है?

EntityManager.getReference()वास्तव में एक त्रुटि प्रवण विधि है और वास्तव में बहुत कम मामले हैं जहां एक क्लाइंट कोड को इसका उपयोग करने की आवश्यकता होती है।
व्यक्तिगत रूप से, मुझे इसका उपयोग करने की कभी आवश्यकता नहीं थी।

EntityManager.getReference () और EntityManager.find (): ओवरहेड के संदर्भ में कोई अंतर नहीं है

मैं स्वीकृत उत्तर और विशेष रूप से असहमत हूं:

तो मैं फोन खोजने के विधि, जेपीए प्रदाता, पर्दे के पीछे, कॉल करेंगे

SELECT NAME, AGE FROM PERSON WHERE PERSON_ID = ?

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?

अगर मैं getReference विधि कहता हूं , पर्दे के पीछे JPA प्रदाता, कॉल करेगा

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?

यह व्यवहार नहीं है जो मुझे हाइबरनेट 5 के साथ मिलता है और जेवाडॉक getReference()ऐसा नहीं कहता है:

एक उदाहरण प्राप्त करें, जिसका राज्य आलसी हो सकता है। यदि अनुरोधित उदाहरण डेटाबेस में मौजूद नहीं है, तो EntityNotFoundException को फेंक दिया जाता है जब उदाहरण स्थिति पहले एक्सेस की जाती है। (दृढ़ता प्रदाता प्रदाता की अनुमति है कि जब EntRNference कहा जाता है तो EntityNotFoundException को फेंक दिया जाए।) आवेदन को यह उम्मीद नहीं करनी चाहिए कि उदाहरण स्थिति टुकड़ी पर उपलब्ध होगी, जब तक कि यह एप्लिकेशन द्वारा एक्सेस नहीं किया गया था जब तक कि इकाई प्रबंधक खुला नहीं था।

EntityManager.getReference() दो मामलों में इकाई को पुनः प्राप्त करने के लिए एक क्वेरी को अलग करता है:

1) यदि इकाई दृढ़ता संदर्भ में संग्रहीत की जाती है, तो यह पहला स्तर कैश है।
और यह व्यवहार के लिए विशिष्ट नहीं है EntityManager.getReference(), EntityManager.find()इकाई को दृढ़ता संदर्भ में संग्रहीत होने पर इकाई को पुनः प्राप्त करने के लिए एक क्वेरी को भी छोड़ देगा।

आप किसी भी उदाहरण के साथ पहले बिंदु की जांच कर सकते हैं।
आप वास्तविक हाइबरनेट कार्यान्वयन पर भी भरोसा कर सकते हैं।
दरअसल, इकाई को लोड करने के लिए वर्ग EntityManager.getReference()की createProxyIfNecessary()विधि पर निर्भर करता है org.hibernate.event.internal.DefaultLoadEventListener
यहाँ इसका कार्यान्वयन है:

private Object createProxyIfNecessary(
        final LoadEvent event,
        final EntityPersister persister,
        final EntityKey keyToLoad,
        final LoadEventListener.LoadType options,
        final PersistenceContext persistenceContext) {
    Object existing = persistenceContext.getEntity( keyToLoad );
    if ( existing != null ) {
        // return existing object or initialized proxy (unless deleted)
        if ( traceEnabled ) {
            LOG.trace( "Entity found in session cache" );
        }
        if ( options.isCheckDeleted() ) {
            EntityEntry entry = persistenceContext.getEntry( existing );
            Status status = entry.getStatus();
            if ( status == Status.DELETED || status == Status.GONE ) {
                return null;
            }
        }
        return existing;
    }
    if ( traceEnabled ) {
        LOG.trace( "Creating new proxy for entity" );
    }
    // return new uninitialized proxy
    Object proxy = persister.createProxy( event.getEntityId(), event.getSession() );
    persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey( keyToLoad );
    persistenceContext.addProxy( keyToLoad, proxy );
    return proxy;
}

दिलचस्प हिस्सा यह है:

Object existing = persistenceContext.getEntity( keyToLoad );

2) हम प्रभावी रूप से इकाई में हेरफेर नहीं करते हैं, करने के लिए गूंज lazily लाए जाने जावाडोक की।
दरअसल, इकाई के प्रभावी लोडिंग को सुनिश्चित करने के लिए, इस पर एक विधि लागू करना आवश्यक है।
तो यह लाभ एक ऐसे परिदृश्य से संबंधित होगा जहां हम इसे उपयोग करने की आवश्यकता के बिना एक इकाई को लोड करना चाहते हैं? अनुप्रयोगों के फ्रेम में, यह आवश्यकता वास्तव में असामान्य है और इसके अलावा getReference()यदि आप अगले भाग को पढ़ते हैं तो व्यवहार भी बहुत भ्रामक है।

EntityManager.find () EntityManager.getReference () से अधिक क्यों

ओवरहेड के संदर्भ में, पिछले बिंदु पर चर्चा की गई getReference()तुलना find()में बेहतर नहीं है ।
तो क्यों एक या दूसरे का उपयोग करें?

आह्वान करने से getReference()एक आलसी भ्रूण वाली इकाई वापस आ सकती है।
यहाँ, आलसी लाने वाले इकाई के रिश्तों को नहीं बल्कि इकाई को संदर्भित करते हैं।
इसका अर्थ है कि यदि हम आह्वान करते हैं getReference()और फिर दृढ़ता का संदर्भ बंद हो जाता है, तो इकाई को कभी भी लोड नहीं किया जा सकता है और इसलिए परिणाम वास्तव में अप्रत्याशित है। उदाहरण के लिए, यदि प्रॉक्सी ऑब्जेक्ट धारावाहिक है, तो आप nullधारावाहिक परिणाम के रूप में एक संदर्भ प्राप्त कर सकते हैं या यदि कोई विधि प्रॉक्सी ऑब्जेक्ट पर लागू होती है, तो अपवाद जैसे LazyInitializationExceptionफेंक दिया जाता है।

इसका मतलब यह है कि उस थ्रो को एक इंस्टेंस को हैंडल EntityNotFoundExceptionकरने के लिए उपयोग getReference()करने का मुख्य कारण है जो डेटाबेस में मौजूद नहीं होता है क्योंकि एंटिटी मौजूद नहीं होने पर त्रुटि स्थिति कभी नहीं हो सकती है।

EntityManager.find()EntityNotFoundExceptionयदि इकाई नहीं मिली है, तो फेंकने की महत्वाकांक्षा नहीं है। इसका व्यवहार सरल और स्पष्ट दोनों है। आपको कभी आश्चर्य नहीं होगा क्योंकि यह हमेशा एक भरी हुई इकाई या null(यदि इकाई नहीं मिली है) लौटाता है, लेकिन कभी भी एक प्रॉक्सी के आकार के तहत एक इकाई नहीं है जो प्रभावी रूप से लोड नहीं हो सकती है।
तो EntityManager.find()सबसे मामलों में इष्ट होना चाहिए।


आपके कारण भ्रामक प्रतिक्रिया के साथ तुलना में भ्रामक है + व्लाद मिहेल्का प्रतिक्रिया + व्लाद मिहल्सिया के लिए मेरी टिप्पणी (शायद यह अंतिम महत्वपूर्ण है)।
adrhc

1
प्रो JPA2 राज्य करता है: "बहुत विशिष्ट स्थिति को देखते हुए जिसमें getReference () का उपयोग किया जा सकता है, ढूंढना () का उपयोग लगभग सभी मामलों में किया जाना चाहिए"।
JL_SO

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

2

मैं चयनित जवाब से असहमत हूं, और जैसा कि davidxxx ने ठीक से बताया है, getReference बिना चयन के गतिशील अपडेशन का ऐसा व्यवहार प्रदान नहीं करता है। मैंने इस उत्तर की वैधता के संबंध में एक प्रश्न पूछा, यहां देखें - हाइबरनेट JPA के getReference () के बाद सेटर का उपयोग किए बिना चयन जारी किए बिना अपडेट नहीं किया जा सकता है

मैंने बहुत ईमानदारी से किसी को भी नहीं देखा है जो वास्तव में उस कार्यक्षमता का उपयोग करता है। कहीं भी। और मुझे समझ नहीं आ रहा है कि यह इतना उलझा हुआ क्यों है।

अब सबसे पहले, कोई फर्क नहीं पड़ता कि आप हाइबरनेट प्रॉक्सी ऑब्जेक्ट, सेटर या गेट्टर पर कॉल करते हैं, एक SQL निकाल दिया जाता है और ऑब्जेक्ट लोड हो जाता है।

लेकिन फिर मैंने सोचा, तो क्या होगा अगर JPA getReference () प्रॉक्सी उस कार्यक्षमता को प्रदान नहीं करता है। मैं सिर्फ अपना प्रॉक्सी लिख सकता हूं।

अब, हम सभी यह तर्क दे सकते हैं कि प्राथमिक कुंजियों पर चयन उतनी ही तेजी से होता है जितना एक क्वेरी प्राप्त कर सकता है और इससे बचने के लिए वास्तव में महान लंबाई पर जाना कुछ नहीं है। लेकिन हम में से जो एक कारण या किसी अन्य के कारण इसे संभाल नहीं सकते हैं, नीचे ऐसे प्रॉक्सी का कार्यान्वयन है। लेकिन इससे पहले कि आप कार्यान्वयन देखें, यह देखें कि यह उपयोग है और इसका उपयोग कितना सरल है।

उपयोग

Order example = ProxyHandler.getReference(Order.class, 3);
example.setType("ABCD");
example.setCost(10);
PersistenceService.save(example);

और यह निम्नलिखित प्रश्न को आग देगा -

UPDATE Order SET type = 'ABCD' and cost = 10 WHERE id = 3;

और यदि आप सम्मिलित करना चाहते हैं, तब भी आप PersistenceService.save (नया आदेश ("a", 2)) कर सकते हैं; और यह एक डालने के रूप में यह चाहिए आग होगा।

कार्यान्वयन

इसे अपने pom.xml में जोड़ें -

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
</dependency>

गतिशील प्रॉक्सी बनाने के लिए इस वर्ग को बनाएं -

@SuppressWarnings("unchecked")
public class ProxyHandler {

public static <T> T getReference(Class<T> classType, Object id) {
    if (!classType.isAnnotationPresent(Entity.class)) {
        throw new ProxyInstantiationException("This is not an entity!");
    }

    try {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(classType);
        enhancer.setCallback(new ProxyMethodInterceptor(classType, id));
        enhancer.setInterfaces((new Class<?>[]{EnhancedProxy.class}));
        return (T) enhancer.create();
    } catch (Exception e) {
        throw new ProxyInstantiationException("Error creating proxy, cause :" + e.getCause());
    }
}

सभी विधियों के साथ एक इंटरफ़ेस बनाएं -

public interface EnhancedProxy {
    public String getJPQLUpdate();
    public HashMap<String, Object> getModifiedFields();
}

अब, एक इंटरसेप्टर बनाएं जो आपको इन तरीकों को अपने प्रॉक्सी पर लागू करने की अनुमति देगा -

import com.anil.app.exception.ProxyInstantiationException;
import javafx.util.Pair;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import javax.persistence.Id;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
/**
* @author Anil Kumar
*/
public class ProxyMethodInterceptor implements MethodInterceptor, EnhancedProxy {

private Object target;
private Object proxy;
private Class classType;
private Pair<String, Object> primaryKey;
private static HashSet<String> enhancedMethods;

ProxyMethodInterceptor(Class classType, Object id) throws IllegalAccessException, InstantiationException {
    this.classType = classType;
    this.target = classType.newInstance();
    this.primaryKey = new Pair<>(getPrimaryKeyField().getName(), id);
}

static {
    enhancedMethods = new HashSet<>();
    for (Method method : EnhancedProxy.class.getDeclaredMethods()) {
        enhancedMethods.add(method.getName());
    }
}

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    //intercept enhanced methods
    if (enhancedMethods.contains(method.getName())) {
        this.proxy = obj;
        return method.invoke(this, args);
    }
    //else invoke super class method
    else
        return proxy.invokeSuper(obj, args);
}

@Override
public HashMap<String, Object> getModifiedFields() {
    HashMap<String, Object> modifiedFields = new HashMap<>();
    try {
        for (Field field : classType.getDeclaredFields()) {

            field.setAccessible(true);

            Object initialValue = field.get(target);
            Object finalValue = field.get(proxy);

            //put if modified
            if (!Objects.equals(initialValue, finalValue)) {
                modifiedFields.put(field.getName(), finalValue);
            }
        }
    } catch (Exception e) {
        return null;
    }
    return modifiedFields;
}

@Override
public String getJPQLUpdate() {
    HashMap<String, Object> modifiedFields = getModifiedFields();
    if (modifiedFields == null || modifiedFields.isEmpty()) {
        return null;
    }
    StringBuilder fieldsToSet = new StringBuilder();
    for (String field : modifiedFields.keySet()) {
        fieldsToSet.append(field).append(" = :").append(field).append(" and ");
    }
    fieldsToSet.setLength(fieldsToSet.length() - 4);
    return "UPDATE "
            + classType.getSimpleName()
            + " SET "
            + fieldsToSet
            + "WHERE "
            + primaryKey.getKey() + " = " + primaryKey.getValue();
}

private Field getPrimaryKeyField() throws ProxyInstantiationException {
    for (Field field : classType.getDeclaredFields()) {
        field.setAccessible(true);
        if (field.isAnnotationPresent(Id.class))
            return field;
    }
    throw new ProxyInstantiationException("Entity class doesn't have a primary key!");
}
}

और अपवाद वर्ग -

public class ProxyInstantiationException extends RuntimeException {
public ProxyInstantiationException(String message) {
    super(message);
}

इस प्रॉक्सी का उपयोग करके सेव करने की सेवा -

@Service
public class PersistenceService {

@PersistenceContext
private EntityManager em;

@Transactional
private void save(Object entity) {
    // update entity for proxies
    if (entity instanceof EnhancedProxy) {
        EnhancedProxy proxy = (EnhancedProxy) entity;
        Query updateQuery = em.createQuery(proxy.getJPQLUpdate());
        for (Entry<String, Object> entry : proxy.getModifiedFields().entrySet()) {
            updateQuery.setParameter(entry.getKey(), entry.getValue());
        }
        updateQuery.executeUpdate();
    // insert otherwise
    } else {
        em.persist(entity);
    }

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