[英]Prevent Dozer from triggering Hibernate lazy loading
我正在使用 Spring 事務,因此當 POJO 到 DTO 轉換發生時,事務仍然處於活動狀態。
我想阻止 Dozer 觸發延遲加載,以便永遠不會發生隱藏的 sql 查詢:所有獲取都必須通過 HQL 顯式完成(以獲得對性能的最佳控制)。
這是一個好習慣(我在任何地方都找不到它的記錄)?
如何安全地做到這一點?
我在 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.