18

我正在使用 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 会话,并且不会阻止延迟加载。

4

6 回答 6

26

我发现用于管理此问题的唯一通用解决方案(在查看自定义转换器、事件侦听器和代理解析器之后)是通过实现自定义字段映射器。我发现这个功能隐藏在 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" />

我希望这可以帮助任何寻找解决方案的人,因为我在搜索 Internet 时没有找到任何示例。

于 2011-05-11T11:38:04.603 回答
10

上面流行版本的变体,确保同时捕获 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;
}

}

于 2013-01-11T19:36:01.177 回答
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));
    }
}
于 2011-09-23T09:05:18.173 回答
4

您是否考虑过完全禁用延迟加载?

它似乎与您声明要使用的模式不符:

我想防止 Dozer 触发延迟加载,以便永远不会发生隐藏的 sql 查询:所有获取都必须通过 HQL 显式完成(以获得对性能的最佳控制)。

这表明您永远不想使用延迟加载。

Dozer 和你传递给它的 Hibernate 支持的 bean 幸福地相互无知;Dozer 所知道的是它正在访问 bean 中的属性,并且 Hibernate 支持的 bean 正在响应对get()延迟加载集合的调用,就像您自己访问这些属性一样。

任何使 Dozer 了解您的 bean 中的 Hibernate 代理或反之亦然的技巧,IMO 都会破坏您的应用程序的层。

如果您不希望在意外时间触发任何“隐藏的 SQL 查询”,只需禁用延迟加载。

于 2011-04-05T13:15:21.723 回答
0

此映射器的简短版本将是

return sourceFieldValue instanceof AbstractPersistentCollection && 
!( (AbstractPersistentCollection) sourceFieldValue ).wasInitialized();
于 2013-03-22T12:45:31.183 回答
0

使用 CustomFieldMapper 可能不是一个好主意,因为它会为源类的每个字段调用,但我们关心的只是惰性关联映射(子对象列表),因此我们可以在实体对象的 getter 中设置 null 值,

public Set<childObject> getChild() {
if(Hibernate.isInitialized(child){
    return childObject;
}else
 return null;
}
于 2014-11-03T11:06:24.343 回答