【问题标题】:Hibernate lazy-load application designHibernate 延迟加载应用程序设计
【发布时间】:2011-06-28 23:02:54
【问题描述】:

我倾向于将HibernateSpring 框架结合使用,它具有声明性事务划分功能(例如@Transactional)。

众所周知,hibernate 尝试尽可能地非侵入性透明,但这证明更具挑战性在使用 lazy-loaded 关系时。


我看到了许多具有不同透明度的设计替代方案。

  1. 使关系不延迟加载(例如,fetchType=FetchType.EAGER)
    • 这违反了延迟加载的整个想法..
  2. 使用Hibernate.initialize(proxyObj); 初始化集合
    • 这意味着与 DAO 的耦合度相对较高
    • 虽然我们可以用initialize 定义一个接口,但不能保证其他实现提供任何等价物。
  3. 将事务行为添加到持久化Model 对象本身(使用dynamic proxy@Transactional
    • 我没有尝试过动态代理方法,尽管我似乎从来没有让@Transactional 处理持久对象本身。可能是由于休眠是在代理上的操作。
    • 在交易实际发生时失去控制
  4. 同时提供惰性/非惰性 API,例如 loadData()loadDataWithDeps()
    • 强制应用程序知道何时使用哪个例程,再次紧密耦合
    • 方法溢出,loadDataWithA(),....,loadDataWithX()
  5. 强制查找依赖项,例如,仅提供 byId() 操作
    • 需要大量非面向对象的例程,例如findZzzById(zid),然后是getYyyIds(zid)而不是z.getY()
    • 如果事务之间的处理开销很大,则一个接一个地获取集合中的每个对象会很有用。
  6. 加入应用程序 @Transactional 而不仅仅是DAO
    • 嵌套事务的可能考虑因素
    • 需要适应事务管理的例程(例如,足够小)
    • 程序影响小,但可能会导致大量交易
  7. 为DAO提供动态fetch profiles,例如loadData(id, fetchProfile);
    • 应用程序必须知道何时使用哪个配置文件
  8. AoP 类型的事务,例如,拦截操作并在必要时执行事务
    • 需要字节码操作或使用代理
    • 执行事务时失去控制
    • 黑魔法,一如既往:)

我错过了任何选项吗?


在尝试将lazy-loaded 关系在您的应用程序设计中的影响降至最低时,您首选哪种方法?

(哦,对不起WoT

【问题讨论】:

标签: java hibernate spring lazy-loading application-design


【解决方案1】:

众所周知,hibernate 尽量做到非侵入性和透明性

我会说最初的假设是错误的。透明持久性是一个神话,因为应用程序总是应该注意实体生命周期和正在加载的对象图的大小。

请注意,Hibernate 无法读取想法,因此如果您知道特定操作需要一组特定的依赖项,则需要以某种方式向 Hibernate 表达您的意图。

从这个角度来看,明确表达这些意图的解决方案(即 2、4 和 7)看起来是合理的,并且不会因缺乏透明度而受到影响。

【讨论】:

  • 您当然是对的,尽可能透明仅到目前为止有效。这些是您选择的一些不错的选择。
  • 恕我直言:完全正确的答案。的确,这是一个神话。顺便说一句:我的投票将支持选项 4 和 7(或完全远离 ORM)
【解决方案2】:

我不确定您暗示的是哪个问题(由懒惰引起),但对我来说,最大的痛苦是避免在我自己的应用程序缓存中丢失会话上下文。典型案例:

  • 对象foo 已加载并放入地图中;
  • 另一个线程从映射中获取这个对象并调用foo.getBar()(以前从未调用过并且被惰性求值的东西);
  • 轰隆隆!

因此,为了解决这个问题,我们制定了许多规则:

  • 尽可能透明地包装会话(例如,OpenSessionInViewFilter 用于 webapps);
  • 具有用于线程/线程池的通用 API,其中 db 会话绑定/取消绑定在层次结构中的某个较高位置(包装在 try/finally 中)完成,因此子类不必考虑它;
  • 在线程之间传递对象时,传递 ID 而不是对象本身。接收线程可以根据需要加载对象;
  • 缓存对象时,从不缓存对象,只缓存它们的 ID。当您知道 ID 时,在您的 DAO 或管理器类中有一个抽象方法可以从二级 Hibernate 缓存加载对象。从二级 Hibernate 缓存中检索对象的成本仍然比使用 DB 便宜得多。

正如您所见,这确实与非侵入性和透明相去甚远。但是成本仍然可以承受,与我必须为急切加载所支付的价格相比。后者的问题在于,有时在加载单个引用对象时会导致蝴蝶效应,更不用说实体集合了。内存消耗、CPU 使用率和延迟至少也更差,所以我想我可以忍受。

【讨论】:

  • 感谢您的回复。 transparency 的丢失是由于强制应用程序关心加载惰性对象。如果一切都被急切地获取,应用程序可能完全不知道对象是否被持久化到数据库中,因为Foo.getBar() 总是会成功。 > when passing objects between threads, pass IDs,是的,这将对应于#5。
【解决方案3】:

如果您正在构建 Web 应用程序,一个非常常见的模式是使用 OpenEntityManagerInViewFilter

如果您正在构建服务,我会在服务的公共方法上打开 TX,而不是在 DAO 上打开 TX,因为一个方法通常需要获取或更新多个实体。

这将解决任何“延迟加载异常”。如果您需要更高级的性能调优,我认为获取配置文件是您的最佳选择。

【讨论】:

  • 我猜你想说:A very common antipattern ...。尽管我同意在服务级别上打开 TX,但使用 OSIV 仍然是一种反模式,并会导致非常严重的问题,例如无法优雅地处理异常或性能下降。总结一下:恕我直言 OSIV 是一个随和的解决方案,但仅适用于玩具项目。
猜你喜欢
  • 2011-03-28
  • 1970-01-01
  • 1970-01-01
  • 2013-12-31
  • 1970-01-01
  • 2011-07-25
  • 2018-11-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多