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

Java 防止推土机触发休眠延迟加载,java,hibernate,spring,dozer,spring-transactions,Java,Hibernate,Spring,Dozer,Spring Transactions,我使用的是Spring事务,因此当POJO到DTO转换发生时,该事务仍然处于活动状态 我想防止Dozer触发延迟加载,这样就不会出现隐藏的sql查询:所有的抓取都必须通过HQL显式完成(以获得对性能的最佳控制) 这是一个好的实践吗(我在任何地方都找不到文档) 如何安全地进行 在DTO转换之前,我尝试了以下方法: PlatformTransactionManager tm = (PlatformTransactionManager) SingletonFactoryProvider.getSing

我使用的是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();
    
    它可以防止延迟加载,但直接在应用程序层(在我的项目中称为“facade”)操纵会话是一种好的做法吗?我应该担心哪些负面影响?(我已经看到,涉及POJO->DTO转换的测试不能再通过AbstractTransactionnalDatasource Spring测试类启动了,因为这些类试图在不再链接到活动会话的事务上触发回滚)


    我还尝试将传播设置为“不受支持”或“需要新”,但它会重用当前的Hibernate会话,并且不会阻止延迟加载。

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

    它似乎并不真的与您声明要使用的模式相协调:

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

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

    推土机和你传递给它的冬眠背脊的豆子幸福地彼此无知;Dozer只知道它正在访问bean中的属性,而支持Hibernate的bean正在响应对
    get()
    延迟加载集合的调用,就像您自己访问这些属性一样

    在我看来,任何让Dozer意识到bean中Hibernate代理的技巧,或者反之亦然,都会破坏应用程序的层次


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

    我找到的唯一管理此问题的通用解决方案(在查看自定义转换器、事件侦听器和代理解析程序后)是通过实现自定义字段映射器。我在DozerAPI中找到了这个功能(我不相信用户指南中有记录)

    一个简单的例子如下:

    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。我这样做是为了在将它们传递给客户机时能够区分空(未加载)集合和空集合。这允许我在客户机中定义通用行为,以使用预加载的集合,或进行另一个服务调用来检索集合(如果需要)。此外,如果您决定急切地加载服务层中的任何集合,那么它们将像往常一样被映射

    我使用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" />
    
    
    ...
    

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

    我没有让上述内容起作用(可能是不同的版本)。然而,这很好

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

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

    这将是一个简短的版本

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

    public Set<childObject> getChild() {
    if(Hibernate.isInitialized(child){
        return childObject;
    }else
     return null;
    }
    
    publicset getChild(){
    如果(休眠。初始化(子级){
    返回子对象;
    }否则
    返回null;
    }
    
    当然,如果可能的话,我想“禁用延迟加载”,但是怎么做呢?我的意思是“default lazy=false”意味着我的所有关联都将被急切地获取,不是吗?是的,或者您可以指定
    lazy=“false”
    在类或属性上,是的,这将导致急切抓取。您必须进行急切抓取或延迟加载;您不能将属性/集合映射到Hibernate,并且只能在一段时间内由Hibernate加载。好的,我希望的是:Hibernate在调用getter时从不自动加载我的集合。Hiber只有当我在HQL查询中使用显式的“连接”来指定我的集合时,nate才会加载我的集合(“例如,从个人连接获取订单”),你能确认Hibernate不可能有这种行为吗?(是否有某种原因…?或者其他流行的框架适合这种行为?)Hibernate的理念是在您请求时将对象返回给您,包括所有集合(可能是惰性加载的),关联等。您所描述的内容更接近SQL层,在SQL层中,您可以对获取的内容和未获取的内容进行一些控制。您最好查看其他数据访问库,例如它可以将查询结果(您自己的规范)映射到ObjectsOrry Matt,这是一个更好的答案(请看下面)。谢谢,这太棒了,我甚至可以确认,除了这里,其他地方都没有记录:另外,在最新版本的Dozer(5.3.0)中,还有其他方法可以做到这一点()我不确定如何重新分解代码以使其可读性降低,这确实为这个答案增加了价值。这实际上是在所有适用的集合上禁用延迟加载,而不仅仅是VO映射。如果您有任何应用程序内部逻辑需要延迟加载此集合,它将只会变为null。这个问题特别重要关于禁用的延迟加载