2011-04-05 210 views
15

我正在使用Spring事務,因此當POJO到DTO轉換髮生時事務仍處於活動狀態。防止Dozer觸發Hibernate延遲加載

我想阻止Dozer觸發延遲加載,這樣就不會發生隱藏的SQL查詢:所有抓取都必須通過HQL明確完成(以獲得對性能的最佳控制)。

  1. 這是一個很好的做法(我無法在任何地方找到它)嗎?

  2. 如何安全地做到這一點?

我DTO轉換之前試過這樣:

PlatformTransactionManager tm = (PlatformTransactionManager) SingletonFactoryProvider.getSingletonFactory().getSingleton("transactionManager"); 
tm.commit(tm.getTransaction(new DefaultTransactionDefinition())); 

我不知道會發生什麼交易,但Hibernate的Session不會被關閉,並且延遲加載仍然存在。

我嘗試這樣做:

SessionFactory sf = (SessionFactory) SingletonFactoryProvider.getSingletonFactory().getSingleton("sessionFactory"); 
sf.getCurrentSession().clear(); 
sf.getCurrentSession().close(); 

,並阻礙了延遲加載,但它是直接在應用層(被稱爲我的項目「門面」)操縱會話的好的做法呢?我應該害怕哪種負面影響? (我已經看到,涉及POJO - > DTO轉換的測試不能再通過AbstractTransactionnalDatasource Spring測試類來啓動,因爲這些類試圖觸發對沒有更多鏈接到活動會話的事務的回滾)。

我也嘗試將傳播設置爲NOT_SUPPORTED或REQUIRES_NEW,但它重用了當前的Hibernate會話,並且不會阻止延遲加載。

回答

24

我發現的唯一通用解決方案(查看自定義轉換器,事件監聽器&代理解析器)是通過實現自定義字段映射器。我發現這個功能隱藏在Dozer API中(我不相信它在用戶指南中有記載)。

一個簡單的例子如下;

public class MyCustomFieldMapper implements CustomFieldMapper 
{ 
    public boolean mapField(Object source, Object destination, Object sourceFieldValue, ClassMap classMap, FieldMap fieldMapping) 
    {  
     // Check if field is a Hibernate collection proxy 
     if (!(sourceFieldValue instanceof AbstractPersistentCollection)) { 
      // Allow dozer to map as normal 
      return false; 
     } 

     // Check if field is already initialized 
     if (((PersistentSet) sourceFieldValue).wasInitialized()) { 
      // Allow dozer to map as normal 
      return false; 
     } 

     // Set destination to null, and tell dozer that the field is mapped 
     destination = null; 
     return true; 
    } 
} 

這將返回任何未初始化PersistentSet對象爲空。我這樣做是爲了當它們傳遞給客戶端時,我可以區分NULL(未加載)集合和空集合。這允許我在客戶端中定義通用行爲,以使用預加載的集合,或者進行另一個服務調用來檢索集合(如果需要)。此外,如果您決定在服務層內急切地加載任何集合,那麼它們將像往常一樣映射。

我用注入春天的自定義字段映射:

<bean id="dozerMapper" class="org.dozer.DozerBeanMapper" lazy-init="false"> 
    <property name="mappingFiles"> 
     ... 
    </property> 
    <property name="customFieldMapper" ref="dozerCustomFieldMapper" /> 
</bean> 
<bean id="dozerCustomFieldMapper" class="my.project.MyCustomFieldMapper" /> 

我希望這可以幫助任何人尋找一個解決方案,因爲我沒有找到任何的例子在互聯網上搜索時。

+0

謝謝,這很好,我甚至可以確認它沒有記錄在其他地方:http://www.google.fr/search?q=CustomFieldMapper+PersistentSet – Tristan 2011-05-12 05:51:48

+1

此外,在最新版本的Dozer(5.3.0 ),還有另外一種方法可以做到這一點(http://sourceforge.net/tracker/?func=detail&aid=2993122&group_id=133517&atid=727370) – Tristan 2011-05-12 06:03:16

+0

'destination = null'這行是無操作的,不知道爲什麼在那兒... – 2012-11-19 09:19:45

3

你認爲完全禁用懶惰加載?

它並沒有真正似乎與你的國家的模式來JIVE你想使用:

我想阻止推土機從觸發延遲加載,使隱藏的SQL查詢永遠不會發生:所有取必須通過HQL明確完成(以獲得對性能的最佳控制)。

這表明你永遠不會想要使用延遲加載。

推土機和你傳遞給它的Hibernate支持的豆子都是對對方無知的;所有Dozer知道的是它正在訪問Bean中的屬性,並且Hibernate支持的Bean正在響應調用get()一個延遲加載的集合,就像你自己訪問這些屬性時一樣。

讓Dozer意識到Bean中的Hibernate代理或反之亦然的任何技巧,IMO都會打破應用程序的各個層次。

如果您不希望在意外時間觸發任何「隱藏SQL查詢」,只需禁用延遲加載。

+0

可以肯定,我想如果可能的話,「禁用延遲加載」,但如何做到這一點?我的意思是「default-lazy = false」意味着我所有的關聯都會被熱切地提取出來嗎? – Tristan 2011-04-05 13:46:20

+0

是的,或者您可以在類或屬性上指定'lazy =「false」,並且這會導致提前獲取。你必須有渴望的提取或延遲加載;你不能用Hibernate映射一個屬性/集合,並且只有部分時間才能通過Hibernate加載它。 – 2011-04-05 13:56:39

+0

好的,我想要的是:當調用getter時,Hibernate不會自動加載我的集合。 Hibernate只在我用HQL查詢中的明確「加入」(例如,「來自Person join fetch Orders」)指定它時才加載我的集合,那麼您是否可以確認這種行爲對於Hibernate是不可能的? (是否有某種原因......或其他適合此行爲的流行框架?) – Tristan 2011-04-05 14:00:20

5

我沒有得到上述工作(可能不同的版本)。然而,這工作得很好

public class HibernateInitializedFieldMapper implements CustomFieldMapper { 
    public boolean mapField(Object source, Object destination, Object sourceFieldValue, ClassMap classMap, FieldMap fieldMapping) { 
     //if field is initialized, Dozer will continue mapping 
     return !Hibernate.isInitialized(sourceFieldValue)); 
    } 
} 
9

上面的普及版的變化,使得一定要抓住這兩個PersistentBags,PersistentSets,你的名字......

public class LazyLoadSensitiveMapper implements CustomFieldMapper { 

public boolean mapField(Object source, Object destination, Object sourceFieldValue, ClassMap classMap, FieldMap fieldMapping) { 
    //if field is initialized, Dozer will continue mapping 

    // Check if field is derived from Persistent Collection 
    if (!(sourceFieldValue instanceof AbstractPersistentCollection)) { 
     // Allow dozer to map as normal 
     return false; 
    } 

    // Check if field is already initialized 
    if (((AbstractPersistentCollection) sourceFieldValue).wasInitialized()) { 
     // Allow dozer to map as normal 
     return false; 
    } 

    return true; 
} 

}

0

短版本的這mapper將是

return sourceFieldValue instanceof AbstractPersistentCollection && 
!((AbstractPersistentCollection) sourceFieldValue).wasInitialized(); 
+1

我不確定如何對代碼進行重新分解以減少可讀性,從而爲此答案增加了價值。 – JamieB 2014-05-29 09:32:51

0

使用CustomFieldMapper可能不是一個好主意,因爲它g恩納調用你的源類的各個領域,但我們關心的是隻有懶關聯映射(子對象列表),所以我們可以設置在實體對象的吸空值,

public Set<childObject> getChild() { 
if(Hibernate.isInitialized(child){ 
    return childObject; 
}else 
return null; 
} 
+0

儘管這不僅對VO映射而言,它可以有效地禁用所有適用集合上的延遲加載。如果您有任何需要延遲加載此集合的應用程序內部邏輯,它將僅爲空。這個問題是專門關於禁用VO映射的延遲加載,而不是完全。 – JamieB 2017-06-20 07:56:22