175

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 loading off.

But later I need to send some of the objects (actually one object) to the GWT client via RPC. And it happens that this concrete object is a proxy. So I need to turn it into a real object. I can't find a method like "materialize" in Hibernate.

How can I turn some of the objects from proxies to reals knowing their class and ID?

At the moment the only solution I see is to evict that object from Hibernate's cache and reload it, but it is really bad for many reasons.

Vlad Mihalcea
  • 103,297
  • 39
  • 432
  • 788
Andrey Minogin
  • 4,222
  • 5
  • 33
  • 55

10 Answers10

242

Here's a method I'm using.

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;
}
Bozho
  • 554,002
  • 136
  • 1,025
  • 1,121
  • 1
    I wanted to do the same thing, so I wrote the proxied instance to an ObjectOutputStream and then read it back from a corresponding ObjectInputStream, and that seemed to do the trick. I'm not sure if it's an efficient approach, but still wondering why it worked... any comments on it will be greatly appreciated. Thanks! – shrini1000 Mar 08 '11 at 11:41
  • @shrini1000 it worked because when serializing initializes the collection (if the session is not yet closed). Also `HibernateProxy` defines a `writeReplace` method to force implementors to do something special during serialization. – Bozho Mar 08 '11 at 11:46
  • 1
    Is there a portable (JPA) way to do this? – Kawu Jan 07 '12 at 15:19
  • why does, Hibernate.initialize throwing lazyInitializeException when I call it? Im just using like: Object o = session.get(MyClass.class, id); Object other = o.getSomeOtherClass(); initializeAndUnproxy(other); – fredcrs Jan 31 '12 at 10:26
  • @fredcrs it happens if the session is closed. – Luca Molteni Feb 23 '12 at 16:38
  • Does `Hibernate.initialize(Object)` implicitly checks if the object has been initialized before? – djmj Oct 26 '12 at 16:18
  • @Bozho i tried using this function, but its still returns the proxy object :( Any idea what might be wrong ?? – Khizar Jan 10 '13 at 06:20
  • But calling `Hibernate.initialize(entity)` will load the complete object-graph for that entity from the database. But what if just the real class with the id set for each proxy is needed. This seems more complicated. – djmj Jun 08 '13 at 08:19
  • You should generally avoid the possibility to have a big object graph fetched. http://techblog.bozho.net/?p=645 – Bozho Jun 10 '13 at 10:50
  • Is this still a valid solution as of Hibernate 4.x? Per docs that I was reading there are ways to disable lazy loading for an entity through XML or Annotations. Please advice. (http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html_single/#persistent-classes-pojo-final-example-disable-proxies-xml) – Satya Oct 24 '15 at 21:56
  • @Bozho, first of all, thank you, I've used your code and it has fixed my problem, but I would like to respectably ask you if you think it's really needed to explicitly throw a `NullPointerException` from the method body. Won't it imply in a further `if-null` validation or an extra `try-catch` block? Another point is: if entity is null, ` Hibernate.initialize(entity)` itself will throw an NPE. What do you think? – Jaumzera Jun 15 '16 at 18:14
  • 7
    you can do the same without your own util class - ```(T)Hibernate.unproxy(entity)``` – panser Jun 30 '17 at 08:34
62

Since Hibernate ORM 5.2.10, you can do it likee this:

Object unproxiedEntity = Hibernate.unproxy(proxy);

Before Hibernate 5.2.10. the simplest way to do that was to use the unproxy method offered by Hibernate internal PersistenceContext implementation:

Object unproxiedEntity = ((SessionImplementor) session)
                         .getPersistenceContext()
                         .unproxy(proxy);
Vlad Mihalcea
  • 103,297
  • 39
  • 432
  • 788
  • Does calling this on a parent entity handle collection fields?? eg, if you have a `Department` with List of `Student`, do you still need to `unproxy(department.getStudents())` - or is it enough to just `unproxy(department)`? – trafalmadorian Nov 28 '19 at 04:01
  • 1
    Only the given Proxy is initialized. It does not cascade to associations, as that could potentially load tons of data if you happen to unproxy a root entity. – Vlad Mihalcea Nov 28 '19 at 04:15
  • However `PersistentContext#unproxy(proxy)` throws an exception if the proxy is uninitialized while `Hibernate.unproxy(proxy)` and `LazyInitializer#getImplementation(proxy)` initialize the proxy if necessary. Just caught a exception due to this difference. ;-) – bgraves May 03 '20 at 13:25
13

Try to use Hibernate.getClass(obj)

Juan Mellado
  • 14,693
  • 5
  • 43
  • 53
Sanek Shu
  • 187
  • 1
  • 2
13

I've written following code which cleans object from proxies (if they are not already initialized)

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

}

I use this function over result of my RPC services (via aspects) and it cleans recursively all result objects from proxies (if they are not initialized).

Kerem Baydoğan
  • 9,716
  • 1
  • 37
  • 49
Sergey Bondarev
  • 260
  • 2
  • 7
  • thanks for sharing this code although it has not covered all use cases cases but it really helpfull... – Prateek Singh Nov 20 '14 at 08:03
  • Correct. It should be updated in according with new cases. You could try things recommended by GWT guys. Look here: http://www.gwtproject.org/articles/using_gwt_with_hibernate.html (see Integration Strategies part). In general they recommend to use DTO or Dozer or Gilead. It will be fine if you'll provide your opinion on this. In my case it looks my code is simplest solution, but not full =(. – Sergey Bondarev Dec 12 '14 at 20:02
  • thanks. where can we get an implementation for "CollectionsUtils.containsTotallyEqual(handledObjects, value)" ? – Ilan.K Aug 20 '15 at 15:40
  • public static boolean containsTotallyEqual(Collection> collection, Object value) { if (isEmpty(collection)) { return false; } for (Object object : collection) { if (object == value) { return true; } } return false; } – Sergey Bondarev Aug 21 '15 at 16:38
  • It's just utility method created by myself – Sergey Bondarev Sep 05 '15 at 12:30
10

The way I recommend with JPA 2 :

Object unproxied  = entityManager.unwrap(SessionImplementor.class).getPersistenceContext().unproxy(proxy);
Yannis JULIENNE
  • 139
  • 1
  • 3
  • 2
    How is your answer different than mine? – Vlad Mihalcea Jan 11 '16 at 04:36
  • I've tried this solution... does not work always if you don't put something like this before the unwrap-command: HibernateProxy hibernateProxy = (HibernateProxy)possibleProxyObject; if (hibernateProxy.getHibernateLazyInitializer().isUninitialized()){ hibernateProxy.getHibernateLazyInitializer().initialize(); } – user3227576 Feb 07 '17 at 11:12
2

The another workaround is to call

Hibernate.initialize(extractedObject.getSubojbectToUnproxy());

Just before closing the session.

0x6B6F77616C74
  • 2,460
  • 7
  • 33
  • 64
2

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!

Sharky
  • 449
  • 4
  • 13
1

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;
}
OndroMih
  • 6,329
  • 1
  • 21
  • 38
  • Your example code seems a bit unclear (or maybe I just need more coffee). Where does EntityWrapper come from? should that be AddressWrapper? And I'm guessing AddressWrapped should say AddressWrapper? Can you clarify this? – Gus Oct 23 '16 at 16:03
  • @Gus, you are right. I corrected the example. Thanks :) – OndroMih Oct 24 '16 at 07:57
1

Starting from Hiebrnate 5.2.10 you can use Hibernate.proxy method to convert a proxy to your real entity:

MyEntity myEntity = (MyEntity) Hibernate.unproxy( proxyMyEntity );
O.Badr
  • 2,045
  • 2
  • 20
  • 27
0

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!

Dmitry
  • 2,878
  • 5
  • 41
  • 63