防止Dozer触发Hibernate延迟加载

我正在使用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(); 

它可以防止延迟加载,但是直接在应用程序层(在我的项目中称为“facade”)中操作会话是一种很好的做法吗? 我应该担心哪些消极副作用? (我已经看到涉及POJO – > DTO转换的测试无法再通过AbstractTransactionnalDatasource Spring测试类启动,因为这些类尝试触发对事务的回滚,该事务不再与活动会话相关联)。

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

我发现管理它的唯一通用解决方案(在查看自定义转换器,事件监听器和代理解析器之后)是通过实现自定义字段映射器。 我发现这个function隐藏在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。 我这样做,以便当它们传递给客户端时,我可以区分NULL(非加载)集合和空集合。 这允许我在客户端中定义通用行为以使用预加载的集合,或者进行另一个服务调用以检索集合(如果需要)。 此外,如果您决定在服务层内急切加载任何集合,那么它们将照常映射。

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

   ...     

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

上面流行版本的一个变种,确保捕获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显式完成(以获得对性能的最佳控制)。

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

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

任何让Dozer知道你的bean中的Hibernate代理的技巧,反之亦然,IMO会打破你的应用层。

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

这个映射器的简短版本将是

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

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

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