it-swarm.com.de

Hibernate Proxy in ein reales Entitätsobjekt konvertieren

Während eines Ruhezustands Session lade ich einige Objekte, und einige davon werden aufgrund von verzögertem Laden als Proxys geladen. Es ist alles in Ordnung und ich möchte das Lazy Loading nicht abschalten.

Später muss ich jedoch einige Objekte (eigentlich ein Objekt) über GPC an den GWT-Client senden. Und es kommt vor, dass dieses konkrete Objekt ein Proxy ist. Also muss ich es in ein reales Objekt verwandeln. Ich finde keine Methode wie "Materialisieren" in Hibernate.

Wie kann ich einige Objekte von Proxys zu Reals machen, die ihre Klasse und ID kennen?

Im Moment ist die einzige Lösung, die ich sehe, das Objekt aus dem Cache von Hibernate zu entfernen und neu zu laden, aber es ist aus vielen Gründen wirklich schlecht.

141
Andrey Minogin

Hier ist eine Methode, die ich verwende. 

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

Wie ich in diesem Artikel erklärt habe, können Sie es seit Hibernate ORM 5.2.10 so machen:

Object unproxiedEntity = Hibernate.unproxy( proxy );

Vor dem Winterschlaf 5.2.10. Der einfachste Weg, dies zu tun, war die Verwendung der unproxy - Methode, die von der internen PersistenceContext-Implementierung von Hibernate angeboten wird:

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

Ich habe folgenden Code geschrieben, der Objekt von Proxys löscht (wenn sie nicht bereits initialisiert sind)

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

}

Ich verwende diese Funktion für das Ergebnis meiner RPC-Dienste (über Aspekte) und bereinigt rekursiv alle Ergebnisobjekte von Proxys (wenn sie nicht initialisiert sind).

13
Sergey Bondarev

Versuchen Sie, Hibernate.getClass(obj) zu verwenden

12
Sanek Shu

So empfehle ich mit JPA 2:

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

Mit Spring Data JPA und Hibernate habe ich Subinterfaces von JpaRepository zum Nachschlagen von Objekten verwendet, die zu einer Typhierarchie gehören, die mithilfe der "Join" -Struktur zugeordnet wurde. Leider wurden in den Abfragen Proxies des Basistyps anstelle von Instanzen der erwarteten konkreten Typen zurückgegeben. Dies hinderte mich daran, die Ergebnisse auf die richtigen Typen zu übertragen. Wie Sie kam ich hierher und suchte nach einem effektiven Weg, um meine Mitglieder zu verurteilen.

Vlad hat die richtige Idee, diese Ergebnisse nicht zu veröffentlichen. Yannis liefert etwas mehr Details. Dazu kommt noch der Rest, wonach Sie suchen könnten:

Mit dem folgenden Code können Sie auf einfache Weise Ihre Proxy-Entitäten aufheben:

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

}

Sie können entweder unproxierte Entites oder Proxy-Entities an die unproxy-Methode übergeben. Wenn sie bereits unproxy sind, werden sie einfach zurückgegeben. Andernfalls werden sie nicht weitergeleitet und zurückgegeben.

Hoffe das hilft!

2
Sharky

Die andere Problemumgehung ist Anrufen 

Hibernate.initialize(extractedObject.getSubojbectToUnproxy());

Kurz bevor die Sitzung geschlossen wird.

1
0x6B6F77616C74

Ich habe eine Lösung gefunden, um eine Klasse mit Standard-Java- und JPA-API zu deproxyieren. Getestet mit Hibernate, erfordert jedoch keine Abhängigkeit von Hibernate und sollte mit allen JPA-Providern funktionieren.

Eine einzige Anforderung - es ist notwendig, die übergeordnete Klasse (Adresse) zu ändern und eine einfache Hilfsmethode hinzuzufügen.

Allgemeine Idee: Fügen Sie der übergeordneten Klasse eine Hilfsmethode hinzu, die sich selbst zurückgibt. Wenn die Methode für Proxy aufgerufen wird, leitet sie den Aufruf an die reale Instanz weiter und gibt diese reale Instanz zurück.

Die Implementierung ist etwas komplexer, da Hibernate erkennt, dass sich die proxied-Klasse selbst zurückgibt und immer noch einen Proxy anstelle einer echten Instanz zurückgibt. Umgehung besteht darin, die zurückgegebene Instanz in eine einfache Wrapper-Klasse zu hüllen, die einen anderen Klassentyp hat als die reale Instanz.

In Code:

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

class AddressWrapper {
    private Address wrappedAddress;
...
}

Verwenden Sie Folgendes, um den Adress-Proxy in eine echte Unterklasse umzuwandeln:

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

Vielen Dank für die vorgeschlagenen Lösungen! Leider funktionierte keiner von ihnen für meinen Fall: Empfangen einer Liste von CLOB-Objekten aus der Oracle-Datenbank über JPA - Hibernate mithilfe einer nativen Abfrage.

Alle vorgeschlagenen Ansätze gaben mir entweder eine ClassCastException oder ein Java-Proxy-Objekt (das tief im Inneren den gewünschten Clob enthielt).

Meine Lösung ist also folgende (basierend auf mehreren oben genannten Ansätzen):

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

Hoffe das hilft jemandem!

0
Dmitry

Ab Hiebrnate 5.2.10 können Sie die Methode Hibernate.proxy verwenden, um einen Proxy in Ihre tatsächliche Entität zu konvertieren:

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