Conversione del proxy Hibernate sull’object quadro reale

Durante la sessione di Hibernate sto caricando alcuni oggetti e alcuni di loro sono caricati come proxy a causa del caricamento lento. Va tutto bene e non voglio scaricare il carico pigro.

Ma più tardi ho bisogno di inviare alcuni oggetti (in realtà un object) al client GWT tramite RPC. E succede che questo object concreto è un proxy. Quindi ho bisogno di trasformarlo in object reale. Non riesco a trovare un metodo come “materializza” in Hibernate.

Come posso trasformare alcuni oggetti da proxy a real con la loro class e ID?

Al momento l’unica soluzione che vedo è di sfrattare quell’object dalla cache di Hibernate e ricaricarlo, ma è davvero brutto per molte ragioni.

Ecco un metodo che sto 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; } 

Ho scritto il seguente codice che pulisce l’object dai proxy (se non sono già inizializzati)

 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 questa funzione sul risultato dei miei servizi RPC (tramite aspetti) e pulisce in modo ricorsivo tutti gli oggetti risultato dai proxy (se non sono inizializzati).

Prova ad usare Hibernate.getClass(obj)

Come ho spiegato in questo articolo , poiché Hibernate ORM 5.2.10, puoi farlo in questo modo:

 Object unproxiedEntity = Hibernate.unproxy( proxy ); 

Prima di Hibernate 5.2.10. Il modo più semplice per farlo era utilizzare il metodo nonproxy offerto dall’implementazione interna PersistenceContext di Hibernate:

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

Il modo in cui raccomando con JPA 2:

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

Con Spring Data JPA e Hibernate, stavo usando le sottointerfacce di JpaRepository per cercare oggetti appartenenti a una gerarchia di tipi mappata usando la strategia “join”. Sfortunatamente, le query restituivano i proxy del tipo base invece delle istanze dei tipi concreti attesi. Questo mi ha impedito di trasmettere i risultati ai tipi corretti. Come te, sono venuto qui alla ricerca di un modo efficace per ottenere le mie entrate senza proiezioni.

Vlad ha l’idea giusta per spodestare questi risultati; Yannis fornisce ulteriori dettagli. Aggiungendo alle loro risposte, ecco il resto di ciò che potresti cercare:

Il seguente codice fornisce un modo semplice per eliminare laproxy delle quadro 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; } } 

È ansible passare le entit non proxy o le entity framework proxy al metodo unproxy . Se sono già privi di risorse, verranno semplicemente restituiti. Altrimenti, non verranno proiettati e restituiti.

Spero che questo ti aiuti!

Un’altra soluzione è chiamare

 Hibernate.initialize(extractedObject.getSubojbectToUnproxy()); 

Poco prima di chiudere la sessione.

Ho trovato una soluzione per deproxy una class utilizzando le API standard Java e JPA. Testato con l’ibernazione, ma non richiede l’ibernazione come dipendenza e dovrebbe funzionare con tutti i provider JPA.

Unico requisito: è necessario modificare la class genitore (indirizzo) e aggiungere un semplice metodo di supporto.

Idea generale: aggiungi il metodo helper alla class genitore che restituisce sé stesso. quando il metodo viene chiamato su proxy, inoltrerà la chiamata all’istanza reale e restituirà questa istanza reale.

L’implementazione è un po ‘più complessa, poiché l’ibernazione riconosce che la class proxy restituisce sé stessa e restituisce comunque proxy anziché istanza reale. La soluzione alternativa consiste nel racchiudere l’istanza restituita in una semplice class wrapper, che ha un tipo di class diverso rispetto all’istanza reale.

Nel codice:

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

Per trasmettere il proxy dell’indirizzo alla sottoclass reale, utilizzare quanto segue:

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

Grazie per le soluzioni suggerite! Sfortunatamente, nessuno di loro ha funzionato per il mio caso: ricevere un elenco di oggetti CLOB dal database Oracle tramite JPA – Hibernate, utilizzando una query nativa.

Tutti gli approcci proposti mi hanno dato un object ClassCastException o appena restituito java Proxy (che conteneva in profondità il Clob desiderato).

Quindi la mia soluzione è la seguente (basata su diversi approcci precedenti):

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

Spero che questo aiuti qualcuno!

A partire da Hiebrnate 5.2.10 è ansible utilizzare il metodo Hibernate.proxy per convertire un proxy nella propria quadro reale:

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