休眠延迟加载应用程序设计

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

众所周知,休眠试图尽可能地非侵入性和透明性,但是在使用懒惰加载的关系时,这被证明是更具挑战性的


我看到许多具有不同透明度的设计选择。

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

我错过了任何选项吗?


在尝试在应用程序设计中最大程度地减少关系的影响时,您首选哪种方法?lazy-loaded

(哦,对不起WoT)


答案 1

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

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

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

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


答案 2

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

  • 对象被加载并放入地图中;foo
  • 另一个线程从映射中获取此对象并调用(以前从未调用过并且被延迟计算的东西);foo.getBar()
  • 繁荣!

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

  • 尽可能透明地包装会话(例如 对于网络应用);OpenSessionInViewFilter
  • 具有用于线程/线程池的通用API,其中数据库会话绑定/取消绑定是在层次结构中的高处(包装)中完成的,因此子类不必考虑它;try/finally
  • 在线程之间传递对象时,请传递 ID 而不是对象本身。如果需要,接收线程可以加载对象;
  • 缓存对象时,切勿缓存对象,而是缓存其 ID。在 DAO 或管理器类中使用抽象方法,以便在知道 ID 时从第 2 级休眠缓存加载对象。从2nd级Hibernate缓存中检索对象的成本仍然比去DB便宜得多。

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


推荐