Как преобразовать Hibernate-прокси в реальный объект

Во время Hibernate Session я загружаю некоторые объекты, и некоторые из них загружаются как прокси из-за отложенной загрузки. Все в порядке, и я не хочу отключать ленивую загрузку.

Но позже мне нужно отправить некоторые объекты (фактически один объект) клиенту GWT через RPC. И бывает, что этот конкретный объект является прокси. Поэтому мне нужно превратить его в настоящий объект. Я не могу найти метод, как "материализовать" в Hibernate.

Как я могу превратить некоторые объекты из прокси в действительные, зная их класс и ID?

На данный момент единственное решение, которое я вижу, - это удалить этот объект из кэша Hibernate и перезагрузить его, но это действительно плохо по многим причинам.

Ответ 1

Вот метод, который я использую.

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

Ответ 2

Как я объяснял в этой статье, начиная с Hibernate ORM 5.2.10, вы можете сделать это следующим образом:

Object unproxiedEntity = Hibernate.unproxy(proxy);

До перехода в спящий режим 5.2.10. самый простой способ сделать это - использовать метод unproxy, предлагаемый внутренней реализацией Hibernate PersistenceContext:

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

Ответ 3

Я написал следующий код, который очищает объект от прокси (если они еще не инициализированы)

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

}

Я использую эту функцию по результатам моих служб RPC (через аспекты) и рекурсивно ретуширует все объекты результатов из прокси (если они не инициализированы).

Ответ 4

Попробуйте использовать Hibernate.getClass(obj)

Ответ 5

Как я рекомендую с JPA 2:

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

Ответ 6

С помощью Spring данных JPA и Hibernate я использовал субинтерфейсы JpaRepository для поиска объектов, принадлежащих иерархии типов, которые были сопоставлены с использованием стратегии объединения. К сожалению, запросы возвращали прокси базового типа вместо экземпляров ожидаемых конкретных типов. Это помешало мне привести результаты к правильным типам. Как и вы, я пришел сюда, пытаясь найти эффективный способ заставить моих людей не обращаться.

Влад имеет правильную идею для снятия этих результатов; Яннис дает немного более подробную информацию. Добавляя к своим ответам, здесь остальное, что вы можете искать:

Следующий код обеспечивает простой способ непроксирования ваших проксированных объектов:

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

}

Вы можете передать либо без права доступа, либо прокси-объекты в метод unproxy. Если они уже не заявлены, они просто будут возвращены. В противном случае они будут освобождены и возвращены.

Надеюсь, это поможет!

Ответ 7

Я нашел решение для депроксирования класса с использованием стандартных Java и JPA API. Протестировано спящим режимом, но не требует спящего режима в качестве зависимости и должно работать со всеми поставщиками JPA.

Единственное требование - его необходимо изменить родительский класс (адрес) и добавить простой вспомогательный метод.

Общая идея: добавьте вспомогательный метод к родительскому классу, который возвращает себя. когда метод называется прокси, он переадресует вызов в реальный экземпляр и возвращает этот реальный экземпляр.

Реализация немного сложнее, поскольку hibernate распознает, что прокси-класс возвращает себя и все еще возвращает прокси вместо реального экземпляра. Обходной способ заключается в том, чтобы обернуть возвращаемый экземпляр в простой класс-оболочку, который имеет другой тип класса, чем реальный экземпляр.

В коде:

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

class AddressWrapper {
    private Address wrappedAddress;
...
}

Чтобы передать адресный прокси в настоящий подкласс, используйте следующую команду:

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

Ответ 8

Другим обходным решением является вызов

Hibernate.initialize(extractedObject.getSubojbectToUnproxy());

Незадолго до закрытия сеанса.

Ответ 9

Благодарим вас за предлагаемые решения! К сожалению, ни один из них не работал для моего случая: получение списка объектов CLOB из базы данных Oracle через JPA - Hibernate с использованием собственного запроса.

Все предложенные подходы дали мне либо ClassCastException, либо только что возвращенный объект прокси Java (который глубоко внутри содержал желаемый Clob).

Итак, мое решение следующее (на основе нескольких подходов):

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

Надеюсь, это поможет кому-то!

Ответ 10

Начиная с Hiebrnate 5.2.10 вы можете использовать Hibernate. proxy для преобразования прокси в ваш реальный объект:

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