防止Dozer触发Hibernate延迟加载
我正在使用 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 测试类启动,因为此类尝试触发不再链接到活动会话的事务的回滚)。
我还尝试将传播设置为 NOT_SUPPORTED 或 REQUIRES_NEW,但它重用当前的 Hibernate 会话,并且不会阻止延迟加载。
I am using Spring transactions so the transaction is still active when POJO to DTO conversion occurs.
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).
Is it a good practice (I can't find it documented anywhere) ?
How to do it safely ?
I tried this before DTO conversion :
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.
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).
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我发现的管理此问题的唯一通用解决方案(在研究了自定义转换器、事件侦听器和代理解析器之后)是通过实现自定义字段映射器。我发现这个功能隐藏在 Dozer API 中(我不相信它记录在用户指南中)。
一个简单的例子如下;
这会将任何未初始化的 PersistentSet 对象返回为 null。我这样做是为了当它们传递给客户端时我可以区分 NULL(未加载)集合和空集合。这允许我在客户端中定义通用行为,以使用预加载的集,或进行另一个服务调用来检索该集(如果需要)。此外,如果您决定急切地加载服务层中的任何集合,那么它们将照常映射。
我使用 spring 注入自定义字段映射器:
我希望这可以帮助任何寻找解决方案的人,因为我在搜索互联网时找不到任何示例。
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).
A simple example is as follows;
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.
I inject the custom field mapper using spring:
I hope this helps anyone searching for a solution for this, as I failed to find any examples when searching the Internet.
上面流行版本的一个变体,确保捕获 PercientBag、PercientSets,凡是你能想到的......
}
A variation on the popular version above, makes sure to catch both PersistentBags, PersistentSets, you name it...
}
我没有让上面的工作(可能是不同的版本)。不过这工作正常
I didn't get the above to work (probably different versions). However this works fine
您是否考虑过完全禁用延迟加载?
它似乎与您所说的想要使用的模式并不相符:
这表明您永远不想使用延迟加载。
Dozer 和您传递给它的 Hibernate 支持的 Bean 彼此完全不了解; Dozer 所知道的是,它正在访问 bean 中的属性,并且 Hibernate 支持的 bean 正在响应对延迟加载集合的 get() 调用,就像您自己访问这些属性一样。
在我看来,任何让 Dozer 知道您的 Bean 中的 Hibernate 代理(反之亦然)的技巧都会破坏您的应用程序的各个层。
如果您不希望在意外时间触发任何“隐藏 SQL 查询”,只需禁用延迟加载即可。
Have you considered disabling lazy loading altogether?
It doesn't really seem to jive with the patterns you state you would like to use:
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.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.
If you don't want any "hidden SQL queries" fired at unexpected times, simply disable lazy-loading.
这个映射器的简短版本将是
Short version of this mapper will be
使用 CustomFieldMapper 可能不是一个好主意,因为它会调用源类的每个字段,但我们关心的只是惰性关联映射(子对象列表),因此我们可以在实体对象的 getter 中设置 null 值,
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,