Hibernate延迟加载应用程序设计

时间:2011-02-17 09:18:26

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

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

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


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

  1. 使关系不是延迟加载的(例如fetchType=FetchType.EAGER)
    • 这违反了延迟加载的整个想法..
  2. 使用Hibernate.initialize(proxyObj);初始化集合
    • 这意味着与DAO相对较高的耦合
    • 虽然我们可以使用initialize定义接口,但不保证其他实现提供任何等效的接口。
  3. 将事务行为添加到持久性Model对象本身(使用dynamic proxy@Transactional
    • 我没有尝试过动态代理方法,虽然我似乎从来没有让@Transactional处理持久性对象本身。可能是因为hibernate是在代理上运行的。
    • 实际进行交易时失去控制权
  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类型的交易,例如拦截操作并在必要时执行交易
    • 需要字节码操作或代理使用
    • 执行交易时失去控制权
    • 黑魔法,一如既往:)
  9. 我错过了任何选项吗?


    在尝试最小化lazy-loaded关系对应用程序设计的影响时,您首选的方法是什么?

    (哦,抱歉WoT

3 个答案:

答案 0 :(得分:26)

  

众所周知,hibernate试图尽可能地非侵入性和透明性

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

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

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

答案 1 :(得分:7)

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

  • 对象foo已加载并放入地图中;
  • 另一个线程从地图中获取此对象并调用foo.getBar()(以前从未调用过并且延迟评估的内容);
  • 动臂<!/ LI>

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

  • 尽可能透明地包装会话(例如,对于webapps OpenSessionInViewFilter);
  • 有线程/线程池的通用API,其中db session bind / unbind在层次结构中的某个位置完成(包含在try/finally中),因此子类不必考虑它;
  • 在线程之间传递对象时,传递ID而不是对象本身。接收线程可以在需要时加载对象;
  • 在缓存对象时,永远不要缓存对象,而是缓存对象。在您知道ID时,在DAO或manager类中有一个抽象方法从第二级Hibernate缓存加载对象。从二级Hibernate缓存中检索对象的成本仍然比去DB要便宜得多。

正如您所看到的,这确实无法接近非侵入性和透明。但是成本仍然可以忍受,与我为急切装载所需支付的价格相比。后者的问题是,有时它会在加载单个引用对象时导致蝴蝶效应,更不用说实体集合了。内存消耗,CPU使用率和提到的延迟也差得多,所以我想我可以忍受它。

答案 2 :(得分:3)

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

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

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