防止推土机触发休眠延迟加载

我正在使用Spring事务,因此当POJO到DTO转换发生时,事务仍然处于活动状态。

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

  1. 这是一个好的做法吗(我在任何地方都找不到它的记录)?

  2. 如何安全地做到这一点?

我在DTO转换之前尝试过这个:

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

我不知道事务会发生什么,但是休眠会话没有关闭,延迟加载仍然发生。

我试过这个:

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

它防止了延迟加载,但是直接在应用程序层(在我的项目中称为“外观”)中操作会话是一种很好的做法吗?我应该担心哪些负面副作用?(我已经看到,涉及POJO ->DTO转换的测试不能再通过抽象事务数据库数据源Spring测试类启动,因为这些类试图在不再链接到活动会话的事务上触发回滚)。

我还尝试将传播设置为NOT_SUPPORTED或REQUIRES_NEW,但它重用了当前的Hibernate会话,并且不会阻止延迟加载。


答案 1

我发现用于管理此的唯一通用解决方案(在研究了自定义转换器,事件侦听器和代理解析器之后)是通过实现自定义字段映射器。我发现此功能隐藏在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(未加载)集合和空集合。这允许我在客户端中定义通用行为,以使用预加载的集合,或进行另一个服务调用来检索该集合(如果需要)。此外,如果您决定热切地加载服务层中的任何集合,则它们将照常进行映射。

我使用弹簧注入自定义字段映射器:

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

我希望这有助于任何寻找解决方案的人,因为我在搜索互联网时找不到任何例子。


答案 2

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

}


推荐