将Hibernate代理转换为真实的对象
在Hibernate会话期间,我正在加载一些对象,其中一些由于延迟加载而被加载为代理。 一切正常,我不想closures延迟加载。
但后来我需要通过RPC将一些对象(实际上是一个对象)发送到GWT客户端。 碰巧这个具体的对象是一个代理。 所以我需要把它变成真正的对象。 在Hibernate中我找不到像“实现”的方法。
我怎样才能把代理的一些对象变成知道他们的类和ID的实体?
目前唯一的解决scheme是从Hibernate的caching中清除对象并重新加载它,但是由于很多原因它确实很糟糕。
这是我正在使用的一种方法。
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; }
尝试使用Hibernate.getClass(obj)
我已经写了下面的代码清除代理对象(如果他们还没有被初始化)
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服务的结果(通过方面),它recursion地清除代理中的所有结果对象(如果它们没有被初始化的话)。
我推荐JPA 2的方式:
Object unproxied = entityManager.unwrap(SessionImplementor.class).getPersistenceContext().unproxy(proxy);
最简单的方法是使用Hibernate内部PersistenceContext
实现提供的unproxy方法:
Object unproxied = ((SessionImplementor)session).getPersistenceContext().unproxy(proxy);
另一个解决方法是打电话
Hibernate.initialize(extractedObject.getSubojbectToUnproxy());
就在会议闭幕之前。
使用Spring Data JPA和Hibernate,我使用JpaRepository
子接口来查找属于使用“join”策略映射的types层次结构的对象。 不幸的是,查询返回的是基types的代理,而不是预期的具体types的实例。 这使我无法将结果转换为正确的types。 像你一样,我来到这里寻找一种有效的方法来让我的衣服变得不讨人喜欢。
弗拉德有正确的想法,取消这些结果; Yannis提供了更多的细节。 添加到他们的答案,这是你可能要找的其余部分:
下面的代码提供了一个简单的方法来代理你的代理实体:
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的实体或代理实体传递给unproxy
方法。 如果他们已经被取消了,他们就会被退回。 否则,他们会得到unproxy和返回。
希望这可以帮助!
我find了一个使用标准的Java和JPA API去除类的解决scheme。 使用hibernate进行testing,但不要求使用hibernate作为依赖项,并且应该与所有JPA提供程序一起工作。
Onle的一个要求 – 它有必要修改父类(地址),并添加一个简单的辅助方法。
总体思路:将辅助方法添加到返回自身的父类。 当方法调用代理时,它会将调用转发给实例并返回这个实例。
实现有点复杂,因为hibernate认识到代理类返回自己,仍然返回代理,而不是实际的实例。 解决方法是将返回的实例包装到一个简单的包装类中,该包装类与真实实例具有不同的类types。
在代码中:
class Address { public AddressWrapper getWrappedSelf() { return new AddressWrapper(this); } ... } class AddressWrapper { private Address wrappedAddress; ... }
要将Address代理转换为实际的子类,请使用以下命令:
Address address = dao.getSomeAddress(...); Address deproxiedAddress = address.getWrappedSelf().getWrappedAddress(); if (deproxiedAddress instanceof WorkAddress) { WorkAddress workAddress = (WorkAddress)deproxiedAddress; }
感谢您提供的解决scheme! 不幸的是,他们都没有为我的情况工作:通过JPA – Hibernate从Oracle数据库接收CLOB对象列表,使用本机查询。
所有提出的方法给了我一个ClassCastException或刚刚返回的java代理对象(它深深地包含所需的Clob)。
所以我的解决scheme如下(基于以上几种方法):
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开始,您可以使用Hibernate.proxy方法将代理转换为您的实体:
MyEntity myEntity = (MyEntity) Hibernate.unproxy( proxyMyEntity );