2010-02-07 68 views
133

在Hibernate会话期间,我加载了一些对象,其中一些由于延迟加载而被加载为代理。没关系,我不想关闭延迟加载。将Hibernate代理转换为真实体对象

但后来我需要通过RPC发送一些对象(实际上是一个对象)到GWT客户端。碰巧这个具体的对象是一个代理。所以我需要把它变成真正的对象。我在Hibernate中找不到像“物化”这样的方法。

如何将代理中的一些对象转换为知道他们的类和ID的实数?

目前我看到的唯一解决方案是从Hibernate的缓存中逐出该对象并重新加载它,但由于很多原因它确实很糟糕。

回答

208

这是我使用的一种方法。

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

我想做同样的事情,所以我写的代理实例到一个ObjectOutputStream,然后读取它从后面一个对应的ObjectInputStream,这似乎是诀窍。我不确定这是否是一种有效的方法,但是仍然想知道为什么它可以工作......对此的任何评论将不胜感激。谢谢! – shrini1000 2011-03-08 11:41:28

+0

@ shrini1000它的工作原理是因为序列化时初始化集合(如果会话尚未关闭)。 'HibernateProxy'定义了一个'writeReplace'方法来强制执行者在序列化过程中做一些特殊的事情。 – Bozho 2011-03-08 11:46:54

+1

有没有便携式(JPA)方式来做到这一点? – Kawu 2012-01-07 15:19:56

12

尝试使用Hibernate.getClass(obj)

+13

这会返回类而不是deproxied对象本身 – 2013-06-20 21:53:06

1

我找到了一个解决方案使用标准的Java和JPA API来deproxy类。使用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; 
} 
+0

你的示例代码似乎有点不清楚(或者我只需要更多的咖啡)。 EntityWrapper从哪里来?应该是AddressWrapper吗?我猜AddressWrapped应该说AddressWrapper?你能澄清这一点吗? – Gus 2016-10-23 16:03:42

+0

@格斯,你说得对。我纠正了这个例子。谢谢:) – OndrejM 2016-10-24 07:57:00

10

我已经写了下面这从代理清除对象(如果它们尚未初始化代码)

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服务的结果(通过方面),它使用c递归地从代理缓存所有结果对象(如果它们未被初始化)。

+0

感谢分享这个代码,虽然它没有覆盖所有的用例,但它真的有帮助... – 2014-11-20 08:03:23

+0

正确。应根据新案例进行更新。你可以尝试GWT家伙推荐的东西。看看这里:http://www.gwtproject.org/articles/using_gwt_with_hibernate.html(见集成策略部分)。一般而言,他们推荐使用DTO或Dozer或Gilead。如果你提供你的意见,这将是很好的。在我的情况下,它看起来我的代码是最简单的解决方案,但不完整=(。 – 2014-12-12 20:02:28

+0

谢谢。我们在哪里可以得到一个实现“CollectionsUtils.containsTotallyEqual(handledObjects,value)”? – 2015-08-20 15:40:58

6

正如我在this article解释,因为Hibernate ORM 5.2.10,你能做到这likee:

Object unproxiedEntity = Hibernate.unproxy(proxy); 

之前休眠5.2.10。要做到这一点里边反最简单的方法是使用由Hibernate内部PersistenceContext实施提供了unproxy方法:

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

我建议使用JPA 2的方式:

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

你的回答与我的不同吗? – 2016-01-11 04:36:07

+0

I'已经尝试过这个解决方案......如果你在unwrap-command之前没有这样的东西,那么这个解决方案总是不起作用:HibernateProxy hibernateProxy =(HibernateProxy)possibleProxyObject; 。 \t \t \t如果(hibernateProxy.getHibernateLazyInitializer()isUninitialized()){ \t \t \t \t hibernateProxy.getHibernateLazyInitializer()初始化(); \t \t \t} – user3227576 2017-02-07 11:12:33

1

的另一种解决方法是调用

Hibernate.initialize(extractedObject.getSubojbectToUnproxy()); 

就在会议结束前。

0

谢谢您的建议解决方案!不幸的是,它们中没有一个适用于我的情况:使用本机查询从JPA-Hibernate接收Oracle数据库中的CLOB对象列表。

所有提出的方法都给了我一个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(); 
} 

希望这会帮助别人!

2

使用Spring Data JPA和Hibernate,我使用JpaRepository的子接口来查找属于使用“join”策略映射的类型层次结构的对象。不幸的是,查询返回的是基类型的代理,而不是预期的具体类型的实例。这阻止了我将结果转换为正确的类型。和你一样,我来到这里寻找一种有效的方式来让我的物品变得没有问题。

弗拉德有正确的想法来取消这些结果; Yannis提供了更多细节。添加到自己的答案,这里的其他人呢,你可能会寻找:

下面的代码提供了一个简单的方法来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; 
    } 

} 

您可以通过其中非代理的entites或代理实体方法unproxy。如果他们已经没有任何问题,他们只会被退回。否则,他们会得不到答复并返回。

希望这会有所帮助!

0

Hiebrnate 5.2.10开始,你可以使用Hibernate.proxy方法把代理转换到你的真正的实体:

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