हाइबरनेट प्रॉक्सी को वास्तविक एंटिटी ऑब्जेक्ट में कैसे बदलें


161

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

लेकिन बाद में मुझे आरपीसी के माध्यम से कुछ वस्तुओं (वास्तव में एक वस्तु) को GWT क्लाइंट को भेजने की आवश्यकता है। और ऐसा होता है कि यह ठोस वस्तु एक छद्म है। इसलिए मुझे इसे एक वास्तविक वस्तु में बदलने की आवश्यकता है। मुझे हाइबरनेट में "मटीरियलिज़" जैसी विधि नहीं मिल सकती है।

मैं परदे के पीछे से वस्तुओं को अपनी कक्षा और आईडी को जानकर कैसे बदल सकता हूं?

फिलहाल जो एकमात्र समाधान मैं देख रहा हूं वह है हाइबरनेट के कैश से उस वस्तु को निकालना और उसे फिर से लोड करना, लेकिन यह कई कारणों से वास्तव में खराब है।

जवाबों:


232

यहाँ एक विधि है जिसका मैं उपयोग कर रहा हूँ।

public static <T> T initializeAndUnproxy(T entity) {
    if (entity == null) {
        throw new 
           NullPointerException("Entity passed for initialization is null");
    }

    Hibernate.initialize(entity);
    if (entity instanceof HibernateProxy) {
        entity = (T) ((HibernateProxy) entity).getHibernateLazyInitializer()
                .getImplementation();
    }
    return entity;
}

1
मैं एक ही काम करना चाहता था, इसलिए मैंने एक ObjectOutputStream को अनुमानित उदाहरण लिखा और फिर उसे इसी ObjectInputStream से वापस पढ़ा, और यह ट्रिक करने लगा। मुझे यकीन नहीं है कि यह एक कुशल दृष्टिकोण है, लेकिन फिर भी सोच रहा था कि यह क्यों काम किया ... इस पर किसी भी टिप्पणी की बहुत सराहना की जाएगी। धन्यवाद!
तीर्थ यात्रा 19

@ shrini1000 ने यह काम किया क्योंकि जब क्रमांकन संग्रह को आरंभ करता है (यदि सत्र अभी तक बंद नहीं हुआ है)। इसके अलावा कार्यान्वयन के लिए कुछ खास करने के लिए मजबूर करने के लिए HibernateProxyएक writeReplaceविधि को परिभाषित करता है ।
बोझो

1
क्या ऐसा करने का कोई पोर्टेबल (JPA) तरीका है?
काऊ

जब मैं इसे कॉल करता हूं, तो Hibernate.initialize, lazyInitializeException को क्यों फेंक रहा है? Im बस का उपयोग कर की तरह: ऑब्जेक्ट ओ = session.get (MyClass.class, id); ऑब्जेक्ट अन्य = o.getSomeOtherClass (); initializeAndUnproxy (अन्य);
फ्रेडक्र्स

6
आप अपने स्वयं के उपयोग वर्ग के बिना भी ऐसा कर सकते हैं -(T)Hibernate.unproxy(entity)
पैन

47

जैसा कि मैंने इस लेख में बताया , चूंकि हाइबरनेट ORM 5.2.10 है , आप इसे इस तरह से कर सकते हैं:

Object unproxiedEntity = Hibernate.unproxy(proxy);

हाइबरनेट से पहले 5.2.10 । सबसे सरल तरीका यह है कि हाइबरनेट आंतरिक कार्यान्वयन द्वारा प्रस्तावित अनप्रोक्सी विधि का उपयोग करना था PersistenceContext:

Object unproxiedEntity = ((SessionImplementor) session)
                         .getPersistenceContext()
                         .unproxy(proxy);

क्या यह एक मूल इकाई को संभालने के संग्रह क्षेत्रों पर बुला रहा है ?? उदाहरण के लिए, यदि आपके पास Departmentसूची है Student, तो क्या आपको अभी भी जरूरत है unproxy(department.getStudents()) - या यह सिर्फ करने के लिए पर्याप्त है unproxy(department)?
ट्रफालमोरियन

1
केवल दिए गए प्रॉक्सी आरंभिक हैं। यह संघों के लिए झरना नहीं है, क्योंकि यदि आप एक रूट इकाई को अनप्रोडिक करते हैं तो संभवतः टन डेटा लोड कर सकते हैं।
व्लाद मिहालसी

हालाँकि, PersistentContext#unproxy(proxy)एक अपवाद को फेंक देता है यदि प्रॉक्सी अनइंस्टॉल किया गया है Hibernate.unproxy(proxy)और LazyInitializer#getImplementation(proxy)यदि आवश्यक हो तो प्रॉक्सी को इनिशियलाइज़ करें बस इस अंतर के कारण एक अपवाद पकड़ा। ;-)
bgraves

13

उपयोग करने का प्रयास करें Hibernate.getClass(obj)


15
यह अपने आप में अनिर्दिष्ट वस्तु के बजाय कक्षा को लौटाता है
स्टीफन

वास्तव में यह समाधान बहुत अच्छा है जब हम उदाहरणों की तुलना के लिए ओबीजी के वर्ग को खोजने की कोशिश कर रहे हैं।
जोआओ रेबेलो

13

मैंने निम्नलिखित कोड लिखा है जो परदे के पीछे से वस्तु को साफ करता है (यदि वे पहले से ही आरंभिक नहीं हैं)

public class PersistenceUtils {

    private static void cleanFromProxies(Object value, List<Object> handledObjects) {
        if ((value != null) && (!isProxy(value)) && !containsTotallyEqual(handledObjects, value)) {
            handledObjects.add(value);
            if (value instanceof Iterable) {
                for (Object item : (Iterable<?>) value) {
                    cleanFromProxies(item, handledObjects);
                }
            } else if (value.getClass().isArray()) {
                for (Object item : (Object[]) value) {
                    cleanFromProxies(item, handledObjects);
                }
            }
            BeanInfo beanInfo = null;
            try {
                beanInfo = Introspector.getBeanInfo(value.getClass());
            } catch (IntrospectionException e) {
                // LOGGER.warn(e.getMessage(), e);
            }
            if (beanInfo != null) {
                for (PropertyDescriptor property : beanInfo.getPropertyDescriptors()) {
                    try {
                        if ((property.getWriteMethod() != null) && (property.getReadMethod() != null)) {
                            Object fieldValue = property.getReadMethod().invoke(value);
                            if (isProxy(fieldValue)) {
                                fieldValue = unproxyObject(fieldValue);
                                property.getWriteMethod().invoke(value, fieldValue);
                            }
                            cleanFromProxies(fieldValue, handledObjects);
                        }
                    } catch (Exception e) {
                        // LOGGER.warn(e.getMessage(), e);
                    }
                }
            }
        }
    }

    public static <T> T cleanFromProxies(T value) {
        T result = unproxyObject(value);
        cleanFromProxies(result, new ArrayList<Object>());
        return result;
    }

    private static boolean containsTotallyEqual(Collection<?> collection, Object value) {
        if (CollectionUtils.isEmpty(collection)) {
            return false;
        }
        for (Object object : collection) {
            if (object == value) {
                return true;
            }
        }
        return false;
    }

    public static boolean isProxy(Object value) {
        if (value == null) {
            return false;
        }
        if ((value instanceof HibernateProxy) || (value instanceof PersistentCollection)) {
            return true;
        }
        return false;
    }

    private static Object unproxyHibernateProxy(HibernateProxy hibernateProxy) {
        Object result = hibernateProxy.writeReplace();
        if (!(result instanceof SerializableProxy)) {
            return result;
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    private static <T> T unproxyObject(T object) {
        if (isProxy(object)) {
            if (object instanceof PersistentCollection) {
                PersistentCollection persistentCollection = (PersistentCollection) object;
                return (T) unproxyPersistentCollection(persistentCollection);
            } else if (object instanceof HibernateProxy) {
                HibernateProxy hibernateProxy = (HibernateProxy) object;
                return (T) unproxyHibernateProxy(hibernateProxy);
            } else {
                return null;
            }
        }
        return object;
    }

    private static Object unproxyPersistentCollection(PersistentCollection persistentCollection) {
        if (persistentCollection instanceof PersistentSet) {
            return unproxyPersistentSet((Map<?, ?>) persistentCollection.getStoredSnapshot());
        }
        return persistentCollection.getStoredSnapshot();
    }

    private static <T> Set<T> unproxyPersistentSet(Map<T, ?> persistenceSet) {
        return new LinkedHashSet<T>(persistenceSet.keySet());
    }

}

मैं अपनी आरपीसी सेवाओं (पहलुओं के माध्यम से) के परिणाम पर इस फ़ंक्शन का उपयोग करता हूं और यह परदे के पीछे से सभी परिणामी वस्तुओं को साफ करता है (यदि वे आरंभिक नहीं हैं)।


इस कोड को साझा करने के लिए धन्यवाद, हालांकि इसमें सभी उपयोग के मामलों को शामिल नहीं किया गया है, लेकिन यह वास्तव में उपयोगी है ...
प्रेटेक सिंह

सही बात। इसे नए मामलों के अनुसार अपडेट किया जाना चाहिए। आप GWT लोगों द्वारा अनुशंसित चीजों की कोशिश कर सकते हैं। यहाँ देखें: gwtproject.org/articles/using_gwt_with_hibernate.html (एकीकरण रणनीतियाँ भाग देखें)। सामान्य तौर पर वे डीटीओ या डोजर या गिलियड का उपयोग करने की सलाह देते हैं। यदि आप इस पर अपनी राय प्रदान करेंगे तो ठीक रहेगा। मेरे मामले में ऐसा लगता है कि मेरा कोड सबसे सरल उपाय है, लेकिन पूरा नहीं = (?)
सर्गेई बोंडरेव

धन्यवाद। हमें "CollectionsUtils.containsTotallyEqual (हैंडल्डऑब्जेक्ट्स, मूल्य)" के लिए एक कार्यान्वयन कहां मिल सकता है?
इलन .K

सार्वजनिक स्थैतिक बूलियन में पूरी तरह से शामिल होता है (संग्रह <?> संग्रह, वस्तु मूल्य) {अगर (isEmpty (संग्रह)) {झूठी वापसी; } के लिए (वस्तु वस्तु: संग्रह) {अगर (वस्तु == मूल्य) {वापसी सच; } } विवरण झूठा है; }
सर्गेई बोंदरेव

यह सिर्फ खुद के द्वारा बनाई गई उपयोगिता पद्धति है
सर्गेई बोंडरेव

10

जिस तरह से मैं जेपीए 2 के साथ सलाह देता हूं:

Object unproxied  = entityManager.unwrap(SessionImplementor.class).getPersistenceContext().unproxy(proxy);

2
आपका उत्तर मेरे से अलग कैसे है?
व्लाद मिहालसी

मैंने इस समाधान की कोशिश की है ... हमेशा काम नहीं करता है यदि आप कुछ ऐसा नहीं रखते हैं, तो इससे पहले कि आप अनट्रैप-कमांड दें: HibernateProxy hibernateProxy = (HibernateProxy) possibleProxyObject; if (hibernateProxy.getHibernateLazyInitializer ()। isUninitialized ()) {hibernateProxy.getHibernateLazyInitializer ()। initialize (); }
user3227576

2

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

व्लाद को इन परिणामों को अनप्रोक्स करने का सही विचार है; यानिस थोड़ा और विस्तार प्रदान करता है। उनके जवाबों को जोड़ते हुए, यहां बाकी की तलाश की जा रही है:

निम्न कोड आपकी अनुमानित संस्थाओं को अनप्रिडिक्ट करने का एक आसान तरीका प्रदान करता है:

import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionImplementor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.JpaContext;
import org.springframework.stereotype.Component;

@Component
public final class JpaHibernateUtil {

    private static JpaContext jpaContext;

    @Autowired
    JpaHibernateUtil(JpaContext jpaContext) {
        JpaHibernateUtil.jpaContext = jpaContext;
    }

    public static <Type> Type unproxy(Type proxied, Class<Type> type) {
        PersistenceContext persistenceContext =
            jpaContext
            .getEntityManagerByManagedType(type)
            .unwrap(SessionImplementor.class)
            .getPersistenceContext();
        Type unproxied = (Type) persistenceContext.unproxyAndReassociate(proxied);
        return unproxied;
    }

}

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

उम्मीद है की यह मदद करेगा!


1

दूसरा वर्कअराउंड कॉल करना है

Hibernate.initialize(extractedObject.getSubojbectToUnproxy());

सत्र बंद करने से पहले।


1

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

ओनली एक आवश्यकता - मूल वर्ग (पता) को संशोधित करने और एक सरल सहायक विधि जोड़ने के लिए इसकी आवश्यकता।

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

कार्यान्वयन थोड़ा अधिक जटिल है, क्योंकि हाइबरनेट यह मानता है कि अनुमानित वर्ग स्वयं रिटर्न करता है और फिर भी वास्तविक उदाहरण के बजाय प्रॉक्सी देता है। वर्कअराउंड लौटे इंस्टेंस को एक साधारण रैपर क्लास में लपेटना है, जिसमें वास्तविक उदाहरण की तुलना में अलग-अलग वर्ग प्रकार हैं।

कोड में:

class Address {
   public AddressWrapper getWrappedSelf() {
       return new AddressWrapper(this);
   }
...
}

class AddressWrapper {
    private Address wrappedAddress;
...
}

पता प्रॉक्सी को वास्तविक उपवर्ग पर ले जाने के लिए, निम्नलिखित का उपयोग करें:

Address address = dao.getSomeAddress(...);
Address deproxiedAddress = address.getWrappedSelf().getWrappedAddress();
if (deproxiedAddress instanceof WorkAddress) {
WorkAddress workAddress = (WorkAddress)deproxiedAddress;
}

आपका उदाहरण कोड थोड़ा अस्पष्ट लगता है (या शायद मुझे बस अधिक कॉफी की आवश्यकता है)। EntityWrapper कहाँ से आता है? क्या वह एड्रेसवर्डर होना चाहिए? और मुझे पता चल रहा है कि AddressWrapped को AddressWrapper कहना चाहिए? क्या आप इसे स्पष्ट कर सकते हैं?
गस

@ जी, आप सही कह रहे हैं। मैंने उदाहरण को सही किया। धन्यवाद :)
7

1

Hiebrnate 5.2.10 से शुरू होकर आप अपनी वास्तविक इकाई के लिए प्रॉक्सी को परिवर्तित करने के लिए Hibernate.proxy विधि का उपयोग कर सकते हैं :

MyEntity myEntity = (MyEntity) Hibernate.unproxy( proxyMyEntity );

0

सुझाए गए समाधान के लिए धन्यवाद! दुर्भाग्य से, उनमें से किसी ने भी मेरे मामले के लिए काम नहीं किया: एक देशी क्वेरी का उपयोग करके जेपीए - हाइबरनेट के माध्यम से ओरेकल डेटाबेस से CLOB ऑब्जेक्ट्स की एक सूची प्राप्त करना।

प्रस्तावित सभी दृष्टिकोणों ने मुझे या तो क्लासकैस्ट एक्ससेप्शन दिया या सिर्फ जावा प्रोक्सी ऑब्जेक्ट लौटाया (जिसमें गहराई से वांछित क्लोब शामिल था)।

तो मेरा समाधान निम्नलिखित है (कई उपरोक्त दृष्टिकोणों के आधार पर):

Query sqlQuery = manager.createNativeQuery(queryStr);
List resultList = sqlQuery.getResultList();
for ( Object resultProxy : resultList ) {
    String unproxiedClob = unproxyClob(resultProxy);
    if ( unproxiedClob != null ) {
       resultCollection.add(unproxiedClob);
    }
}

private String unproxyClob(Object proxy) {
    try {
        BeanInfo beanInfo = Introspector.getBeanInfo(proxy.getClass());
        for (PropertyDescriptor property : beanInfo.getPropertyDescriptors()) {
            Method readMethod = property.getReadMethod();
            if ( readMethod.getName().contains("getWrappedClob") ) {
                Object result = readMethod.invoke(proxy);
                return clobToString((Clob) result);
            }
        }
    }
    catch (InvocationTargetException | IntrospectionException | IllegalAccessException | SQLException | IOException e) {
        LOG.error("Unable to unproxy CLOB value.", e);
    }
    return null;
}

private String clobToString(Clob data) throws SQLException, IOException {
    StringBuilder sb = new StringBuilder();
    Reader reader = data.getCharacterStream();
    BufferedReader br = new BufferedReader(reader);

    String line;
    while( null != (line = br.readLine()) ) {
        sb.append(line);
    }
    br.close();

    return sb.toString();
}

आशा है कि यह किसी की मदद करेगा!

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