How to convert a Hibernate proxy to a real entity object

后端 未结 10 711
离开以前
离开以前 2020-11-22 16:49

During a Hibernate Session, I am loading some objects and some of them are loaded as proxies due to lazy loading. It\'s all OK and I don\'t want to turn lazy lo

相关标签:
10条回答
  • 2020-11-22 17:47

    The another workaround is to call

    Hibernate.initialize(extractedObject.getSubojbectToUnproxy());
    

    Just before closing the session.

    0 讨论(0)
  • 2020-11-22 17:47

    I found a solution to deproxy a class using standard Java and JPA API. Tested with hibernate, but does not require hibernate as a dependency and should work with all JPA providers.

    Onle one requirement - its necessary to modify parent class (Address) and add a simple helper method.

    General idea: add helper method to parent class which returns itself. when method called on proxy, it will forward the call to real instance and return this real instance.

    Implementation is a little bit more complex, as hibernate recognizes that proxied class returns itself and still returns proxy instead of real instance. Workaround is to wrap returned instance into a simple wrapper class, which has different class type than the real instance.

    In code:

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

    To cast Address proxy to real subclass, use following:

    Address address = dao.getSomeAddress(...);
    Address deproxiedAddress = address.getWrappedSelf().getWrappedAddress();
    if (deproxiedAddress instanceof WorkAddress) {
    WorkAddress workAddress = (WorkAddress)deproxiedAddress;
    }
    
    0 讨论(0)
  • 2020-11-22 17:47

    Thank you for the suggested solutions! Unfortunately, none of them worked for my case: receiving a list of CLOB objects from Oracle database through JPA - Hibernate, using a native query.

    All of the proposed approaches gave me either a ClassCastException or just returned java Proxy object (which deeply inside contained the desired Clob).

    So my solution is the following (based on several above approaches):

    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();
    }
    

    Hope this will help somebody!

    0 讨论(0)
  • 2020-11-22 17:51

    With Spring Data JPA and Hibernate, I was using subinterfaces of JpaRepository to look up objects belonging to a type hierarchy that was mapped using the "join" strategy. Unfortunately, the queries were returning proxies of the base type instead of instances of the expected concrete types. This prevented me from casting the results to the correct types. Like you, I came here looking for an effective way to get my entites unproxied.

    Vlad has the right idea for unproxying these results; Yannis provides a little more detail. Adding to their answers, here's the rest of what you might be looking for:

    The following code provides an easy way to unproxy your proxied entities:

    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;
        }
    
    }
    

    You can pass either unproxied entites or proxied entities to the unproxy method. If they are already unproxied, they'll simply be returned. Otherwise, they'll get unproxied and returned.

    Hope this helps!

    0 讨论(0)
提交回复
热议问题