Преобразование гибернации приближение объекта
во время сеанса гибернации я загружаю некоторые объекты, и некоторые из них загружаются как прокси из-за ленивой загрузки. Все в порядке, и я не хочу отключать ленивую загрузку.
но позже мне нужно отправить некоторые объекты (фактически один объект) клиенту GWT через RPC. И бывает, что этот конкретный объект является прокси. Поэтому мне нужно превратить его в реальный объект. Я не могу найти такой метод, как "материализоваться" в спящем режиме.
Как я могу превратить некоторые объекты из прокси чтобы реалы знали свой класс и ID?
на данный момент единственное решение, которое я вижу, - это выселить этот объект из кэша Hibernate и перезагрузить его, но это действительно плохо по многим причинам.
10 ответов:
вот метод, который я использую.
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; }
Я написал следующий код, который очищает объект от прокси (если они еще не инициализированы)
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 (через аспекты), и она рекурсивно очищает все объекты результата от прокси (если они не инициализированы).
как я объяснил в в этой статье, так как Hibernate ORM 5.2.10, вы можете сделать это так:
Object unproxiedEntity = Hibernate.unproxy( proxy );
Перед Гибернацией 5.2.10. Самый простой способ сделать это-использовать unproxy метод, предлагаемый Hibernate internal
PersistenceContext
реализация:Object unproxiedEntity = ((SessionImplementor) session) .getPersistenceContext() .unproxy(proxy);
Как я рекомендую с JPA 2:
Object unproxied = entityManager.unwrap(SessionImplementor.class).getPersistenceContext().unproxy(proxy);
С Spring Data JPA и Hibernate, я использовал подинтерфейсы
JpaRepository
для поиска объектов, принадлежащих к иерархии типов, которая была отображена с помощью стратегии "join". К сожалению, запросы возвращали прокси базового типа вместо экземпляров ожидаемых конкретных типов. Это помешало мне привести результаты к правильным типам. Как и вы, я пришел сюда в поисках эффективного способа получить мои entites unproxied.Влад имеет правильную идею для unproxying эти результаты; Яннис предоставляет немного больше деталей. Добавляя к их ответам, вот остальная часть того, что вы могли бы искать:
следующий код обеспечивает простой способ unproxy проксируемых объектов:
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
метод. Если они уже непроксированы, они просто будут возвращены. В противном случае, они получат непроксированные и возвращенные.надеюсь, что это помогает!
другой обходной путь-вызвать
Hibernate.initialize(extractedObject.getSubojbectToUnproxy());
непосредственно перед закрытием сессии.
Я нашел решение для депрокси класса с использованием стандартных Java и JPA API. Тестируется с помощью hibernate, но не требует hibernate в качестве зависимости и должен работать со всеми поставщиками JPA.
Onle одно требование - его необходимо изменить родительский класс (адрес) и добавить простой вспомогательный метод.
общая идея: добавить вспомогательный метод в родительский класс, который возвращает сама. когда метод вызывается на прокси, он будет перенаправлять вызов на реальный экземпляр и возвращать этот реальный пример.
реализация немного сложнее, так как 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; }
Спасибо за предложенные решения! К сожалению, ни один из них не работал для моего случая: получение списка объектов 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(); }
надеюсь, что это будет помогите кто-нибудь!
начиная от Hiebrnate 5.2.10 можно использовать спящий режим.доверенности метод преобразования прокси в ваш реальный объект:
MyEntity myEntity = (MyEntity) Hibernate.unproxy( proxyMyEntity );