java 防止 Dozer 触发 Hibernate 延迟加载
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5552379/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Prevent Dozer from triggering Hibernate lazy loading
提问by Tristan
I am using Spring transactions so the transaction is still active when POJO to DTO conversion occurs.
我正在使用 Spring 事务,因此当 POJO 到 DTO 转换发生时,事务仍然处于活动状态。
I would like to prevent Dozer from triggering lazy loading, so that hidden sql queries never occur : all fetching has to be done explicitly via HQL (to get the best control on performances).
我想防止 Dozer 触发延迟加载,以便永远不会发生隐藏的 sql 查询:所有获取都必须通过 HQL 显式完成(以获得对性能的最佳控制)。
Is it a good practice (I can't find it documented anywhere) ?
How to do it safely ?
这是一个好习惯吗(我在任何地方都找不到它的记录)?
如何安全地做到这一点?
I tried this before DTO conversion :
我在 DTO 转换之前试过这个:
PlatformTransactionManager tm = (PlatformTransactionManager) SingletonFactoryProvider.getSingletonFactory().getSingleton("transactionManager");
tm.commit(tm.getTransaction(new DefaultTransactionDefinition()));
I don't know what happens to the transaction, but the Hibernate session doesn't get closed, and the lazy loading still occurs.
我不知道事务会发生什么,但是 Hibernate 会话没有关闭,延迟加载仍然发生。
I tried this :
我试过这个:
SessionFactory sf = (SessionFactory) SingletonFactoryProvider.getSingletonFactory().getSingleton("sessionFactory");
sf.getCurrentSession().clear();
sf.getCurrentSession().close();
And it prevents lazy loading, but is it a good practice to manipulate session directly in the application layer (which is called "facade" in my project) ? Which negative side effects should I fear ? (I've already seen that tests involving POJO -> DTO conversions could no more be launched through AbstractTransactionnalDatasource Spring test classes, because this classes try to trigger a rollback on a transaction which is no more linked to an active session).
它可以防止延迟加载,但是直接在应用程序层(在我的项目中称为“外观”)中操作会话是否是一种好习惯?我应该担心哪些负面影响?(我已经看到涉及 POJO -> DTO 转换的测试不能再通过 AbstractTransactionnalDatasource Spring 测试类启动,因为这些类尝试触发不再链接到活动会话的事务的回滚)。
I've also tried to set propagation to NOT_SUPPORTED or REQUIRES_NEW, but it reuse the current Hibernate session, and doesn't prevent lazy loading.
我还尝试将传播设置为 NOT_SUPPORTED 或 REQUIRES_NEW,但它会重用当前的 Hibernate 会话,并且不会阻止延迟加载。
回答by JamieB
The only generic solution I have found for managing this (after looking into Custom Converters, Event Listeners & Proxy Resolvers) is by implementing a Custom Field Mapper. I found this functionality tucked away in the Dozer API (I don't believe it is documented in the User Guide).
我发现的唯一通用解决方案(在研究自定义转换器、事件侦听器和代理解析器之后)是通过实现自定义字段映射器来管理它。我发现这个功能隐藏在 Dozer API 中(我认为它没有记录在用户指南中)。
A simple example is as follows;
一个简单的例子如下;
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;
}
}
This will return any non-initialized PersistentSet objects as null. I do this so that when they are passed to the client I can differentiate between a NULL (non-loaded) collection and an empty collection. This allows me to define generic behaviour in the client to either use the pre-loaded set, or make another service call to retrieve the set (if required). Additionally, if you decide to eagerly load any collections within the service layer then they will be mapped as usual.
这会将任何未初始化的 PersistentSet 对象返回为 null。我这样做是为了在将它们传递给客户端时,我可以区分 NULL(未加载)集合和空集合。这允许我在客户端中定义通用行为以使用预加载的集合,或者进行另一个服务调用来检索集合(如果需要)。此外,如果您决定急切地加载服务层中的任何集合,那么它们将照常映射。
I inject the custom field mapper using spring:
我使用 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" />
I hope this helps anyone searching for a solution for this, as I failed to find any examples when searching the Internet.
我希望这可以帮助任何为此寻找解决方案的人,因为我在搜索 Internet 时没有找到任何示例。
回答by Alex Rose
A variation on the popular version above, makes sure to catch both PersistentBags, PersistentSets, you name it...
上面流行版本的一个变体,确保同时捕获 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;
}
}
}
回答by user959107
I didn't get the above to work (probably different versions). However this works fine
我没有让上述工作(可能是不同的版本)。但是这很好用
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));
}
}
回答by matt b
Have you considered disabling lazy loading altogether?
您是否考虑过完全禁用延迟加载?
It doesn't really seem to jive with the patterns you state you would like to use:
它似乎并不真正符合您声明要使用的模式:
I would like to prevent Dozer from triggering lazy loading, so that hidden sql queries never occur : all fetching has to be done explicitly via HQL (to get the best control on performances).
我想防止 Dozer 触发延迟加载,以便永远不会发生隐藏的 sql 查询:所有获取都必须通过 HQL 显式完成(以获得对性能的最佳控制)。
This suggests you would never want to use lazy loading.
这表明您永远不想使用延迟加载。
Dozer and the Hibernate-backed beans you pass to it are blissfully ignorant of each other; all Dozer knows is that it is accessing properties in the bean, and the Hibernate-backed bean is responding to calls to get()
a lazy-loaded collection just as it would if you were accessing those properties yourself.
Dozer 和你传递给它的 Hibernate 支持的 beans 幸福地相互无知;Dozer 只知道它正在访问 bean 中的属性,并且 Hibernate 支持的 bean 正在响应对get()
延迟加载的集合的调用,就像您自己访问这些属性一样。
Any tricks to make Dozer aware of the Hibernate proxies in your beans or vice versa would, IMO, break down the layers of your app.
任何让 Dozer 知道你的 bean 中的 Hibernate 代理的技巧,反之亦然,IMO 会分解你的应用程序的层。
If you don't want any "hidden SQL queries" fired at unexpected times, simply disable lazy-loading.
如果您不希望在意外时间触发任何“隐藏的 SQL 查询”,只需禁用延迟加载。
回答by asm0dey
回答by Prateek Singh
Using CustomFieldMapper may not be a good idea as it gonna invoke for every field of your source class,but our concern is only lazy association mapping(child object list),so we can set the null value in getter of the entity object,
使用 CustomFieldMapper 可能不是一个好主意,因为它会为源类的每个字段调用,但我们关心的只是延迟关联映射(子对象列表),因此我们可以在实体对象的 getter 中设置空值,
public Set<childObject> getChild() {
if(Hibernate.isInitialized(child){
return childObject;
}else
return null;
}