How to convert a Hibernate proxy to a real entity object
Asked Answered
C

11

191

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.

Contrite answered 7/2, 2010 at 9:31 Comment(0)
C
260

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;
}
Charmaincharmaine answered 7/2, 2010 at 9:52 Comment(13)
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!Weingarten
@Weingarten 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.Charmaincharmaine
Is there a portable (JPA) way to do this?Bougie
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);Simpatico
@Simpatico it happens if the session is closed.Adulterant
Does Hibernate.initialize(Object) implicitly checks if the object has been initialized before?Scowl
@Charmaincharmaine i tried using this function, but its still returns the proxy object :( Any idea what might be wrong ??Geraint
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.Scowl
You should generally avoid the possibility to have a big object graph fetched. techblog.bozho.net/?p=645Charmaincharmaine
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. (docs.jboss.org/hibernate/orm/4.3/manual/en-US/html_single/…)Chaplain
@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?Halpern
you can do the same without your own util class - (T)Hibernate.unproxy(entity)Owen
this throws an error: "could not initialize proxy- no Session".Fishery
C
88

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);
Certain answered 12/3, 2015 at 7:51 Comment(5)
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)?Chacha
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.Certain
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. ;-)Mythomania
how would you unproxy a collection of entities?Mir
The same way. It should work for entity collections too.Certain
S
18

Try to use Hibernate.getClass(obj)

Skipbomb answered 8/4, 2012 at 10:2 Comment(3)
This returns the class rather than the deproxied object itselfMalikamalin
Actually this solution is great when we are trying to find the Class of obj for instanceof comparisons.Vtol
Note that the javadoc states: "This operation will initialize a proxy by side-effect."Candice
C
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).

Cantrip answered 23/9, 2014 at 17:57 Comment(5)
thanks for sharing this code although it has not covered all use cases cases but it really helpfull...Torrent
Correct. It should be updated in according with new cases. You could try things recommended by GWT guys. Look here: 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 =(.Cantrip
thanks. where can we get an implementation for "CollectionsUtils.containsTotallyEqual(handledObjects, value)" ?Schiedam
public static boolean containsTotallyEqual(Collection<?> collection, Object value) { if (isEmpty(collection)) { return false; } for (Object object : collection) { if (object == value) { return true; } } return false; }Cantrip
It's just utility method created by myselfCantrip
S
10

The way I recommend with JPA 2 :

Object unproxied  = entityManager.unwrap(SessionImplementor.class).getPersistenceContext().unproxy(proxy);
Stclair answered 27/8, 2015 at 14:1 Comment(2)
How is your answer different than mine?Certain
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(); }Acanthus
B
6

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 );
Barcellona answered 11/11, 2017 at 23:45 Comment(0)
O
2

The another workaround is to call

Hibernate.initialize(extractedObject.getSubojbectToUnproxy());

Just before closing the session.

Oldfangled answered 25/9, 2015 at 23:49 Comment(0)
E
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!

Everlasting answered 14/6, 2016 at 23:41 Comment(0)
I
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;
}
Involution answered 2/7, 2013 at 8:17 Comment(2)
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?Greybeard
@Gus, you are right. I corrected the example. Thanks :)Involution
T
1

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!

Thalassa answered 9/2, 2016 at 16:11 Comment(0)
G
0

For those who need a "pure" JPA solution: If you don't mind modifying your entity, you can add a simple method to it:

class MyEntity {
  //...
  public MyEntity unproxy() {
    return this;
  }
}

This works because once calling this method against the proxy, the proxy will call the "real" method on the entity (which returns the entity and not the proxy).

Be are of this side-effect, however: NHibernate warning Narrowing proxy to - this operation breaks ==

Garnet answered 20/3 at 14:53 Comment(1)
Thanks for answering this in 14 years :) I am so glad that jpa and hibernate are not actual any more (at least for me). Using jOOQ for many years already and happy with that.Contrite

© 2022 - 2024 — McMap. All rights reserved.