web-dev-qa-db-fra.com

Conversion du proxy Hibernate en un objet entité réel

Au cours d'une Hibernation Session, je charge certains objets et certains d'entre eux sont chargés en tant que mandataires en raison d'un chargement paresseux. Tout va bien et je ne veux pas arrêter le chargement paresseux.

Mais plus tard, je dois envoyer certains des objets (en fait un seul objet) au client GWT via RPC. Et il se trouve que cet objet concret est un proxy. J'ai donc besoin de le transformer en un objet réel. Je ne trouve pas de méthode comme "matérialiser" dans Hibernate.

Comment puis-je transformer certains objets des proxies en réels connaissant leur classe et leur ID?

Pour le moment, la seule solution que je vois est d'expulser cet objet du cache d'Hibernate et de le recharger, mais c'est vraiment grave pour plusieurs raisons.

141
Andrey Minogin

Voici une méthode que j'utilise. 

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

Comme je l'ai expliqué dans cet article , depuis Hibernate ORM 5.2.10, vous pouvez le faire comme ceci:

Object unproxiedEntity = Hibernate.unproxy( proxy );

Avant Hibernate 5.2.10. La façon la plus simple de le faire était d’utiliser la méthode unfoxy proposée par Hibernate interne PersistenceContext:

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

J'ai écrit le code suivant qui nettoie l'objet des mandataires (s'ils ne sont pas déjà initialisés)

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

}

J'utilise cette fonction sur le résultat de mes services RPC (via aspects) et nettoie de manière récursive tous les objets de résultat des mandataires (s'ils ne sont pas initialisés).

13
Sergey Bondarev

Essayez d'utiliser Hibernate.getClass(obj)

12
Sanek Shu

La façon dont je recommande avec JPA 2:

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

Avec Spring Data JPA et Hibernate, j'utilisais des sous-interfaces de JpaRepository pour rechercher des objets appartenant à une hiérarchie de types mappée à l'aide de la stratégie "join". Malheureusement, les requêtes renvoyaient des mandataires du type de base au lieu d'instances des types concrets attendus. Cela m'a empêché de transmettre les résultats aux types corrects. Comme vous, je suis venu ici à la recherche d’un moyen efficace de récupérer mes données personnelles.

Vlad a la bonne idée pour supprimer ces résultats. Yannis fournit un peu plus de détails. En plus de leurs réponses, voici le reste de ce que vous pourriez rechercher:

Le code suivant fournit un moyen facile de dé-proxy vos entités mandatées:

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

}

Vous pouvez transmettre des entités non proxy ou des entités proxy à la méthode unproxy. S'ils ne sont pas prêts, ils seront simplement renvoyés. Sinon, ils seront désapprouvés et renvoyés.

J'espère que cela t'aides!

2
Sharky

L’autre solution consiste à appeler 

Hibernate.initialize(extractedObject.getSubojbectToUnproxy());

Juste avant de fermer la session.

1
0x6B6F77616C74

J'ai trouvé une solution pour supprimer une classe en utilisant les API standard Java et JPA. Testé avec hibernate, mais ne nécessite pas hibernate en tant que dépendance et doit fonctionner avec tous les fournisseurs JPA.

Une seule exigence - il est nécessaire de modifier la classe parente (Address) et d’ajouter une méthode d’aide simple.

Idée générale: ajoutez la méthode d'assistance à la classe parente qui se retourne elle-même. Lorsque la méthode est appelée sur un proxy, elle transfère l'appel à une instance réelle et renvoie cette instance réelle.

L'implémentation est un peu plus complexe, car hibernate reconnaît que la classe mandatée se retourne elle-même et renvoie toujours un proxy plutôt qu'une instance réelle La solution consiste à incorporer l'instance renvoyée dans une classe wrapper simple, dont le type de classe est différent de celui de l'instance réelle.

Dans du code:

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

class AddressWrapper {
    private Address wrappedAddress;
...
}

Pour convertir le proxy d’adresse en sous-classe réelle, utilisez les éléments suivants:

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

Merci pour les solutions suggérées! Malheureusement, aucun d'entre eux n'a fonctionné dans mon cas: recevoir une liste d'objets CLOB de la base de données Oracle via JPA - Hibernate, à l'aide d'une requête native.

Toutes les approches proposées m'ont donné soit un ClassCastException, soit un objet Java Proxy tout juste renvoyé (qui contenait profondément le Clob souhaité).

Ma solution est donc la suivante (basée sur plusieurs approches ci-dessus):

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

J'espère que cela aidera quelqu'un!

0
Dmitry

À partir de Hiebrnate 5.2.10 vous pouvez utiliser Hibernate.proxy méthode pour convertir un proxy en votre entité réelle:

MyEntity myEntity = (MyEntity) Hibernate.unproxy( proxyMyEntity );
0
O.Badr