Hibernate延迟加载应用程序设计


87

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

众所周知,休眠试图尽可能做到非侵入性透明性,但是事实证明,在使用关系时这更具挑战性lazy-loaded


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

  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



您能否提供选项4的示例?
degreesightdc

Answers:


26

众所周知,冬眠试图做到无创且尽可能透明

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

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

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


您当然是对的,到目前为止,尽可能透明是可行的。这些都是您追求的不错的选择。
JohanSjöberg

恕我直言:完全正确的答案。确实,这是一个神话。顺便说一句:我的投票将是对选项4和7的投票(或完全放弃ORM)
G. Demecki 2015年

7

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

  • 对象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。
JohanSjöberg

3

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

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

这将解决任何“延迟加载异常”。如果您需要更高级的功能来进行性能调整,我认为获取配置文件是必经之路。


1
我猜你想说的话:一个非常普遍的反模式...。尽管我同意在服务级别上打开TX,但是使用OSIV仍然是一种反模式,并导致非常严重的问题,例如无法妥善处理异常或性能下降。总结:IMHO OSIV是一个随和的解决方案,但仅对玩具项目有用。
G. Demecki 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.