我倾向于将Hibernate与Spring框架结合使用,并且它具有声明性事务划分功能(例如,@Transactional) .
众所周知,hibernate试图尽可能地非侵入性和透明性,但是当使用 lazy-loaded 关系时,这证明了 a bit more challenging .
我看到了许多具有不同透明度的设计方案 .
-
使关系不是延迟加载的(例如,
fetchType=FetchType.EAGER)
-
这违反了延迟加载的整个想法..
-
使用
Hibernate.initialize(proxyObj);
初始化集合 -
这意味着与DAO的相对高耦合
-
虽然我们可以使用
initialize
定义接口,但不保证其他实现提供任何等效的接口 . -
将事务行为添加到持久性
Model
对象本身(使用dynamic proxy或@Transactional
) -
我没有尝试过动态代理方法,虽然我似乎从来没有让@Transactional处理持久性对象本身 . 可能是因为hibernate是在代理上运行的 .
-
实际发生交易时失去控制权
-
提供延迟/非延迟API,例如
loadData()
和loadDataWithDeps()
-
强制应用程序知道何时使用哪个例程,再次紧耦合
-
方法溢出,
loadDataWithA()
,....,loadDataWithX()
-
强制查找依赖项,例如,仅提供
byId()
操作 -
需要大量非面向对象的例程,例如
findZzzById(zid)
,然后getYyyIds(zid)
而不是z.getY()
-
如果事务之间存在大量处理开销,则逐个获取集合中的每个对象会很有用 .
-
创建@Transactional应用程序的一部分,而不仅仅是DAO
-
嵌套事务的可能考虑因素
-
需要适用于事务管理的例程(例如,足够小)
-
小的程序化影响,尽管可能会导致大量交易
-
为DAO提供动态fetch profiles,例如
loadData(id, fetchProfile);
-
应用程序必须知道何时使用哪个配置文件
-
AoP类型的交易,例如拦截操作并在必要时执行交易
-
需要字节码操作或代理使用
-
执行交易时失去控制权
-
黑魔法,一如既往:)
我错过了任何选择吗?
在尝试最小化应用程序设计中 lazy-loaded
关系的影响时,您最喜欢哪种方法?
(哦,抱歉WoT)
3 回答
我会说最初的假设是错误的 . 转换持久性是一个神话,因为应用程序总是应该关注实体生命周期和被加载的对象图的大小 .
请注意,Hibernate无法读取想法,因此如果您知道某个特定操作需要一组特定的依赖关系,那么您需要以某种方式表达您对Hibernate的意图 .
从这个角度来看,明确表达这些意图的解决方案(即2,4和7)看起来是合理的,并且不会缺乏透明度 .
我不确定你暗示哪个问题(由懒惰引起),但对我来说最大的痛苦是避免在我自己的应用程序缓存中丢失会话上下文 . 典型:
object
foo
已加载并放入 Map 中;另一个线程从 Map 中获取此对象并调用
foo.getBar()
(之前从未调用过的东西并且是惰性求值的);热潮!
因此,为了解决这个问题,我们有许多规则:
尽可能透明地包装会话(例如
OpenSessionInViewFilter
for webapps);有线程/线程池的通用API,其中db session bind / unbind在层次结构中的某个位置完成(包含在
try/finally
中),因此子类不必考虑它;在线程之间传递对象时,传递ID而不是对象本身 . 接收线程可以在需要时加载对象;
缓存对象时,永远不要缓存对象,而是缓存对象 . 在您知道ID时,在DAO或manager类中有一个抽象方法从第二级Hibernate缓存加载对象 . 从二级Hibernate缓存中检索对象的成本仍然比去DB要便宜得多 .
正如您所看到的,这确实无法接近非侵入性和透明性 . 但成本仍然可以忍受,与我为急切加载所需支付的价格相比较 . 后者的问题是,有时它会在加载单个引用对象时导致蝴蝶效应,更不用说实体集合了 . 内存消耗,CPU使用率和延迟提到最少也差得多,所以我想我可以忍受它 .
如果您正在构建Web应用程序,则非常常见的模式是使用OpenEntityManagerInViewFilter .
如果您正在构建服务,我会在服务的公共方法上打开TX,而不是在DAO上打开TX,因为通常需要获取或更新多个实体 .
这将解决任何“延迟加载异常” . 如果您需要更高级的性能调优,我认为获取配置文件是可行的方法 .