Conversión de proxy de Hibernate a un objeto de entidad real

Durante la sesión de Hibernate, estoy cargando algunos objetos y algunos de ellos se cargan como proxies debido a la carga diferida. Todo está bien y no quiero apagar la carga.

Pero luego tengo que enviar algunos de los objetos (en realidad, un objeto) al cliente GWT a través de RPC. Y sucede que este objeto concreto es un proxy. Entonces necesito convertirlo en un objeto real. No puedo encontrar un método como “materializar” en Hibernate.

¿Cómo puedo convertir algunos objetos de los servidores proxy a los reales conociendo su clase y su ID?

Por el momento, la única solución que veo es desalojar ese objeto de la memoria caché de Hibernate y volver a cargarlo, pero es realmente malo por muchas razones.

Este es un método que estoy usando.

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

He escrito el siguiente código que limpia el objeto de los proxies (si aún no están inicializados)

 public class PersistenceUtils { private static void cleanFromProxies(Object value, List 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 cleanFromProxies(T value) { T result = unproxyObject(value); cleanFromProxies(result, new ArrayList()); 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 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  Set unproxyPersistentSet(Map persistenceSet) { return new LinkedHashSet(persistenceSet.keySet()); } } 

Uso esta función sobre el resultado de mis servicios RPC (a través de aspectos) y limpia recursivamente todos los objetos resultantes de los proxies (si no están inicializados).

Intenta usar Hibernate.getClass(obj)

Como expliqué en este artículo , desde Hibernate ORM 5.2.10, puede hacerlo así:

 Object unproxiedEntity = Hibernate.unproxy( proxy ); 

Antes de Hibernate 5.2.10. La forma más sencilla de hacerlo fue utilizar el método no proxy ofrecido por la implementación interna de PersistenceContext Hibernate:

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

La forma en que lo recomiendo con JPA 2:

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

Con Spring Data JPA e Hibernate, estaba usando subinterfaces de JpaRepository para buscar objetos pertenecientes a una jerarquía de tipos que se mapeó utilizando la estrategia “join”. Desafortunadamente, las consultas devolvían proxies del tipo base en lugar de instancias de los tipos de concreto esperados. Esto me impidió arrojar los resultados a los tipos correctos. Al igual que usted, vine aquí en busca de una forma efectiva de obtener mis entidades sin seguimiento.

Vlad tiene la idea correcta para desprogtwigr estos resultados; Yannis proporciona un poco más de detalle. Agregando a sus respuestas, aquí está el rest de lo que podría estar buscando:

El siguiente código proporciona una manera fácil de desproxylar sus entidades proxy:

 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 unproxy(Type proxied, Class type) { PersistenceContext persistenceContext = jpaContext .getEntityManagerByManagedType(type) .unwrap(SessionImplementor.class) .getPersistenceContext(); Type unproxied = (Type) persistenceContext.unproxyAndReassociate(proxied); return unproxied; } } 

Puede pasar entidades sin proxy o entidades con proxy al método no unproxy . Si ya no están procesados, simplemente serán devueltos. De lo contrario, se desprogtwigrán y se devolverán.

¡Espero que esto ayude!

La otra solución es llamar

 Hibernate.initialize(extractedObject.getSubojbectToUnproxy()); 

Justo antes de cerrar la sesión.

Encontré una solución para desproxylar una clase usando la API Java y JPA estándar. Probado con hibernación, pero no requiere hibernación como dependencia y debería funcionar con todos los proveedores de JPA.

Solo un requisito: es necesario modificar la clase principal (Dirección) y agregar un método de ayuda simple.

Idea general: agregar el método auxiliar a la clase padre que se devuelve a sí mismo. cuando el método requirió proxy, reenviará la llamada a instancia real y devolverá esta instancia real.

La implementación es un poco más compleja, ya que hibernate reconoce que la clase proxied se devuelve a sí misma y aún devuelve el proxy en lugar de la instancia real. La solución consiste en envolver la instancia devuelta en una clase contenedora simple, que tiene un tipo de clase diferente a la instancia real.

En codigo:

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

Para convertir el proxy de dirección en una subclase real, use lo siguiente:

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

¡Gracias por las soluciones sugeridas! Desafortunadamente, ninguno de ellos funcionó para mi caso: recibir una lista de objetos CLOB de la base de datos Oracle a través de JPA – Hibernate, usando una consulta nativa.

Todos los enfoques propuestos me dieron un ClassCastException o simplemente un objeto Java Proxy devuelto (que profundamente contenía el Clob deseado).

Entonces mi solución es la siguiente (basada en varios enfoques anteriores):

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

Espero que esto ayude a alguien!

A partir de Hiebrnate 5.2.10 puede usar el método Hibernate.proxy para convertir un proxy en su entidad real:

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