Hibernate 延迟加载应用程序设计

我倾向于结合使用 冬眠春天框架及其声明性事务划分功能(例如,@ 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关系的影响时,您首选的方法是什么?

(对于 沃特我很抱歉)

22205 次浏览

A very common pattern is to use OpenEntityManagerInViewFilter if you're building a web application.

If you're building a service, I would open the TX on the public method of the service, rather than on the DAOs, as very often a method requires to get or update several entities.

This will solve any "Lazy Load exception". If you need something more advanced for performance tuning, I think fetch profiles is the way to go.

As we all known, hibernate tries to be as non-invasive and as transparent as possible

I would say the initial assumption is wrong. Transaparent persistence is a myth, since application always should take care of entity lifecycle and of size of object graph being loaded.

Note that Hibernate can't read thoughts, therefore if you know that you need a particular set of dependencies for a particular operation, you need to express your intentions to Hibernate somehow.

From this point of view, solutions that express these intentions explicitly (namely, 2, 4 and 7) look reasonable and don't suffer from the lack of transparency.

I am not sure which problem (caused by lazyness) you're hinting to, but for me the biggest pain is to avoid losing session context in my own application caches. Typical case:

  • object foo is loaded and put into a map;
  • another thread takes this object from the map and calls foo.getBar() (something that was never called before and is lazy evaluated);
  • boom!

So, to address this we have a number of rules:

  • wrap sessions as transparently as possible (e.g. OpenSessionInViewFilter for webapps);
  • have common API for threads/thread pools where db session bind/unbind is done somewhere high in the hierarchy (wrapped in try/finally) so subclasses don't have to think about it;
  • when passing objects between threads, pass IDs instead of objects themselves. Receiving thread can load object if it needs to;
  • when caching objects, never cache objects but their ids. Have an abstract method in your DAO or manager class to load the object from 2nd level Hibernate cache when you know the ID. The cost of retrieving objects from 2nd level Hibernate cache is still far cheaper than going to DB.

This, as you can see, is indeed nowhere close to non-invasive and transparent. But the cost is still bearable, to compare with the price I'd have to pay for eager loading. The problem with latter is that sometimes it leads to the butterfly effect when loading single referenced object, let alone a collection of entities. Memory consumption, CPU usage and latency to mention the least are also far worse, so I guess I can live with it.