2011-04-05 223 views
15

我正在使用Spring事务,因此当POJO到DTO转换发生时事务仍处于活动状态。防止Dozer触发Hibernate延迟加载

我想阻止Dozer触发延迟加载,这样就不会发生隐藏的SQL查询:所有抓取都必须通过HQL明确完成(以获得对性能的最佳控制)。

  1. 这是一个很好的做法(我无法在任何地方找到它)吗?

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

我DTO转换之前试过这样:

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

我不知道会发生什么交易,但Hibernate的Session不会被关闭,并且延迟加载仍然存在。

我尝试这样做:

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

,并阻碍了延迟加载,但它是直接在应用层(被称为我的项目“门面”)操纵会话的好的做法呢?我应该害怕哪种负面影响? (我已经看到,涉及POJO - > DTO转换的测试不能再通过AbstractTransactionnalDatasource Spring测试类来启动,因为这些类试图触发对没有更多链接到活动会话的事务的回滚)。

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

回答

24

我发现的唯一通用解决方案(查看自定义转换器,事件监听器&代理解析器)是通过实现自定义字段映射器。我发现这个功能隐藏在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 (((PersistentSet) 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(未加载)集合和空集合。这允许我在客户端中定义通用行为,以使用预加载的集合,或者进行另一个服务调用来检索集合(如果需要)。此外,如果您决定在服务层内急切地加载任何集合,那么它们将像往常一样映射。

我用注入春天的自定义字段映射:

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

我希望这可以帮助任何人寻找一个解决方案,因为我没有找到任何的例子在互联网上搜索时。

+0

谢谢,这很好,我甚至可以确认它没有记录在其他地方:http://www.google.fr/search?q=CustomFieldMapper+PersistentSet – Tristan 2011-05-12 05:51:48

+1

此外,在最新版本的Dozer(5.3.0 ),还有另外一种方法可以做到这一点(http://sourceforge.net/tracker/?func=detail&aid=2993122&group_id=133517&atid=727370) – Tristan 2011-05-12 06:03:16

+0

'destination = null'这行是无操作的,不知道为什么在那儿... – 2012-11-19 09:19:45

3

你认为完全禁用懒惰加载?

它并没有真正似乎与你的国家的模式来JIVE你想使用:

我想阻止推土机从触发延迟加载,使隐藏的SQL查询永远不会发生:所有取必须通过HQL明确完成(以获得对性能的最佳控制)。

这表明你永远不会想要使用延迟加载。

推土机和你传递给它的Hibernate支持的豆子都是对对方无知的;所有Dozer知道的是它正在访问Bean中的属性,并且Hibernate支持的Bean正在响应调用get()一个延迟加载的集合,就像你自己访问这些属性时一样。

让Dozer意识到Bean中的Hibernate代理或反之亦然的任何技巧,IMO都会打破应用程序的各个层次。

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

+0

可以肯定,我想如果可能的话,“禁用延迟加载”,但如何做到这一点?我的意思是“default-lazy = false”意味着我所有的关联都会被热切地提取出来吗? – Tristan 2011-04-05 13:46:20

+0

是的,或者您可以在类或属性上指定'lazy =“false”,并且这会导致提前获取。你必须有渴望的提取或延迟加载;你不能用Hibernate映射一个属性/集合,并且只有部分时间才能通过Hibernate加载它。 – 2011-04-05 13:56:39

+0

好的,我想要的是:当调用getter时,Hibernate不会自动加载我的集合。 Hibernate只在我用HQL查询中的明确“加入”(例如,“来自Person join fetch Orders”)指定它时才加载我的集合,那么您是否可以确认这种行为对于Hibernate是不可能的? (是否有某种原因......或其他适合此行为的流行框架?) – Tristan 2011-04-05 14:00:20

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

上面的普及版的变化,使得一定要抓住这两个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; 
} 

}

0

短版本的这mapper将是

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

我不确定如何对代码进行重新分解以减少可读性,从而为此答案增加了价值。 – JamieB 2014-05-29 09:32:51

0

使用CustomFieldMapper可能不是一个好主意,因为它g恩纳调用你的源类的各个领域,但我们关心的是只有懒关联映射(子对象列表),所以我们可以设置在实体对象的吸空值,

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

尽管这不仅对VO映射而言,它可以有效地禁用所有适用集合上的延迟加载。如果您有任何需要延迟加载此集合的应用程序内部逻辑,它将仅为空。这个问题是专门关于禁用VO映射的延迟加载,而不是完全。 – JamieB 2017-06-20 07:56:22