簡體   English   中英

防止 Dozer 觸發 Hibernate 延遲加載

[英]Prevent Dozer from triggering Hibernate lazy loading

我正在使用 Spring 事務,因此當 POJO 到 DTO 轉換發生時,事務仍然處於活動狀態。

我想阻止 Dozer 觸發延遲加載,以便永遠不會發生隱藏的 sql 查詢:所有獲取都必須通過 HQL 顯式完成(以獲得對性能的最佳控制)。

  1. 這是一個好習慣(我在任何地方都找不到它的記錄)?

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

我在 DTO 轉換之前試過這個:

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

我不知道事務會發生什么,但是 Hibernate 會話沒有關閉,延遲加載仍然發生。

我試過這個:

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

它可以防止延遲加載,但是直接在應用程序層(在我的項目中稱為“外觀”)中操作會話是否是一種好習慣? 我應該擔心哪些負面影響? (我已經看到涉及 POJO -> DTO 轉換的測試不能再通過 AbstractTransactionnalDatasource Spring 測試類啟動,因為這些類嘗試觸發不再鏈接到活動會話的事務的回滾)。

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

我找到的唯一通用解決方案(在查看自定義轉換器、事件偵聽器和代理解析器之后)是通過實現自定義字段映射器來管理此問題。 我發現這個功能隱藏在 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 (((AbstractPersistentCollection) 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。 我這樣做是為了在將它們傳遞給客戶端時,我可以區分 NULL(未加載)集合和空集合。 這允許我在客戶端中定義通用行為以使用預加載的集合,或者進行另一個服務調用來檢索集合(如果需要)。 此外,如果您決定急切地加載服務層中的任何集合,那么它們將照常映射。

我使用 spring 注入自定義字段映射器:

<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" />

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

上面流行版本的一個變體,確保同時捕獲 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;
}

}

我沒有讓上述工作(可能是不同的版本)。 但是這很好用

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));
    }
}

您是否考慮過完全禁用延遲加載?

它似乎並沒有真正符合您聲明要使用的模式:

我想阻止 Dozer 觸發延遲加載,以便永遠不會發生隱藏的 sql 查詢:所有獲取都必須通過 HQL 顯式完成(以獲得對性能的最佳控制)。

這表明您永遠不想使用延遲加載。

Dozer 和你傳遞給它的 Hibernate 支持的 beans 幸福地相互不知道; Dozer 只知道它正在訪問 bean 中的屬性,並且 Hibernate 支持的 bean 正在響應對get()延遲加載集合的調用,就像您自己訪問這些屬性一樣。

任何讓 Dozer 意識到你的 bean 中的 Hibernate 代理的技巧,反之亦然,IMO 會分解你的應用程序的層。

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

映射器的簡短版本將是

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

使用 CustomFieldMapper 可能不是一個好主意,因為它會為源類的每個字段調用,但我們關心的只是延遲關聯映射(子對象列表),因此我們可以在實體對象的 getter 中設置空值,

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM