java - initialize - lazy loading hibernate spring




Convertendo o proxy do Hibernate para um objeto de entidade real (7)

Durante uma Session Hibernate, estou carregando alguns objetos 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 objetos (na verdade, um objeto) para o cliente GWT via RPC. E acontece que esse objeto concreto é um proxy. Então eu preciso transformá-lo em um objeto real. Não consigo encontrar um método como "materializar" no Hibernate.

Como posso transformar alguns dos objetos de proxies em reais sabendo sua classe e ID?

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


A maneira que eu recomendo com o JPA 2:

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

A outra solução alternativa é chamar

Hibernate.initialize(extractedObject.getSubojbectToUnproxy());

Apenas antes de fechar a sessão.


Aqui está um método que estou usando.

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

Com o Spring Data JPA e o Hibernate, eu estava usando subinterfaces do JpaRepository para procurar objetos 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. Como você, eu vim aqui procurando uma maneira efetiva de deixar meus entos 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> Type unproxy(Type proxied, Class<Type> 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!


Eu encontrei uma solução para deproxy uma classe usando Java e JPA API padrão. 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 classe pai (Address) e adicionar um método auxiliar simples.

Idéia geral: adicione o método auxiliar à classe 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 classe proxied retorna a si mesma e ainda retorna o proxy em vez da instância real. A solução alternativa é agrupar a instância retornada em uma classe wrapper simples, que possui um tipo de classe 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 subclasse real, use o seguinte:

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

Eu escrevi o código a seguir, que limpa o objeto de proxies (se eles ainda não foram inicializados)

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

}

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


Tente usar o Hibernate.getClass(obj)





lazy-loading