2010-02-07 22 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); 
相關問題