我正在使用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测试类启动,因为这些类尝试触发对不再链接到活动会话的事务的回滚。) p>
我也尝试将传播设置为NOT_SUPPORTED或REQUIRES_NEW,但它重用当前的Hibernate会话,并且不会阻止延迟加载。
答案 0 :(得分: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。我这样做,以便当它们传递给客户端时,我可以区分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" />
我希望这有助于任何人为此寻找解决方案,因为我在搜索互联网时未能找到任何示例。
答案 1 :(得分: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;
}
}
答案 2 :(得分: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));
}
}
答案 3 :(得分:3)
您是否考虑过完全禁用延迟加载?
你似乎并不喜欢使用你想要使用的模式:
我想阻止Dozer触发延迟加载,以便隐藏的sql查询永远不会发生:所有提取都必须通过HQL显式完成(以获得对性能的最佳控制)。
这表明你永远不会想要使用延迟加载。
推土机和你传给它的Hibernate支持的豆子彼此无知;所有Dozer都知道它正在访问bean中的属性,而Hibernate支持的bean正在响应对get()
一个延迟加载集合的调用,就像你自己访问这些属性一样。
让Dozer了解你的bean中的Hibernate代理的任何技巧,反之亦然,IMO会打破你的应用层。
如果您不希望在意外时间触发任何“隐藏的SQL查询”,只需禁用延迟加载。
答案 4 :(得分:0)
此mapper的简短版本将是
return sourceFieldValue instanceof AbstractPersistentCollection &&
!( (AbstractPersistentCollection) sourceFieldValue ).wasInitialized();
答案 5 :(得分:0)
使用CustomFieldMapper可能不是一个好主意,因为它会为源类的每个字段调用,但我们关注的只是惰性关联映射(子对象列表),所以我们可以在实体对象的getter中设置null值,
public Set<childObject> getChild() {
if(Hibernate.isInitialized(child){
return childObject;
}else
return null;
}