Convertendo o proxy do Hibernate para o object de entidade real

Durante a session do Hibernate, estou carregando alguns objects e alguns deles são carregados como proxies devido ao carregamento lento. Está tudo bem e eu não quero deixar o carregamento lento.

Mas depois eu preciso enviar alguns dos objects (na verdade, um object) para o cliente GWT via RPC. E acontece que esse object concreto é um proxy. Então eu preciso transformá-lo em object real. Não consigo encontrar um método como “materializar” no Hibernate.

Como posso transformar alguns dos objects de proxies em reais sabendo sua class e ID?

No momento, a única solução que vejo é remover esse object do cache do Hibernate e recarregá-lo, mas é muito ruim por vários motivos.

    Aqui está um método que estou 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; } 

    Eu escrevi o código a seguir, que limpa o object de proxies (se eles ainda não foram 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()); } } 

    Eu uso essa function sobre o resultado de meus serviços RPC (via aspectos) e limpa recursivamente todos os objects de resultado de proxies (se eles não forem inicializados).

    Tente usar o Hibernate.getClass(obj)

    Como expliquei neste artigo , desde o Hibernate ORM 5.2.10, você pode fazer isso como:

     Object unproxiedEntity = Hibernate.unproxy( proxy ); 

    Antes do Hibernate 5.2.10. A maneira mais simples de fazer isso foi usar o método unproxy oferecido pela implementação do PersistenceContext interno do Hibernate:

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

    A maneira que eu recomendo com o JPA 2:

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

    Com o Spring Data JPA e o Hibernate, eu estava usando subinterfaces do JpaRepository para procurar objects pertencentes a uma hierarquia de tipos que foi mapeada usando a estratégia “join”. Infelizmente, as consultas estavam retornando proxies do tipo base em vez de instâncias dos tipos concretos esperados. Isso me impediu de lançar os resultados para os tipos corretos. Assim como você, eu vim aqui procurando uma maneira efetiva de deixar meus contatos desprotegidos.

    Vlad tem a ideia certa para desproteger esses resultados; Yannis fornece um pouco mais de detalhes. Somando às suas respostas, aqui está o resto do que você pode estar procurando:

    O código a seguir fornece uma maneira fácil de desproxy suas entidades com 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; } } 

    Você pode passar entidades não-proxy ou proxy para o método não- unproxy . Se eles já estiverem desprotegidos, eles simplesmente serão devolvidos. Caso contrário, eles ficarão desprotegidos e retornados.

    Espero que isto ajude!

    A outra solução alternativa é chamar

     Hibernate.initialize(extractedObject.getSubojbectToUnproxy()); 

    Apenas antes de fechar a session.

    Eu encontrei uma solução para deproxy uma class usando API padrão Java e JPA. Testado com hibernação, mas não requer hibernação como dependência e deve funcionar com todos os provedores de JPA.

    Um único requisito – é necessário modificar a class pai (Address) e adicionar um método auxiliar simples.

    Idéia geral: adicione o método auxiliar à class pai que retorna a si mesma. Quando o método é chamado no proxy, ele encaminhará a chamada para a instância real e retornará essa instância real.

    A implementação é um pouco mais complexa, pois o hibernate reconhece que a class proxied retorna a si mesma e ainda retorna o proxy em vez da instância real. A solução alternativa é include a instância retornada em uma class wrapper simples, que possui um tipo de class diferente da instância real.

    Em código:

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

    Para converter o proxy de endereço em subclass real, use o seguinte:

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

    Obrigado pelas soluções sugeridas! Infelizmente, nenhum deles funcionou no meu caso: recebendo uma lista de objects CLOB do database Oracle através do JPA – Hibernate, usando uma consulta nativa.

    Todas as abordagens propostas me deram um ClassCastException ou apenas retornaram o object java Proxy (que continha profundamente o Clob desejado).

    Então, minha solução é a seguinte (com base em várias abordagens acima):

     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 isso ajude alguém!

    A partir do Hiebrnate 5.2.10 você pode usar o método Hibernate.proxy para converter um proxy em sua entidade real:

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