hibernate之优化抓取(定义全局抓取计划)
1.对象获取选项
Hibernate提供了下列方法从数据库中获取对象:
. 导航对象图。
. 通过标识符获取。
. HQL
. Hibernate Criteria接口
. 原生sql查询
我们这里不想详细讨论每一种获取方法。我们更感兴趣的是所谓的默认抓取计划和抓取策略。默认的抓取计划和抓取策略是应用到特定的实体关联或者集合的计划和策略。换句话说,它定义了当加载自己的实体对象,以及访问一个被关联的对象或者集合时,被关联的对象或者集合是否以及如何被加载。每一种获取方法都可能使用不同的计划和策略---也就是说,计划定义了持久化对象网络的哪一部分应该被获取和怎样获取。你的目标是为应用程序中的每个用例找到最好的获取方法和抓取策略;同时也要最小化sql查询的次数,以获得最好的性能。(注意:关于HQL,它常用于对象获取,而不是更新,插入或者删除数据。对象状态同步是持久化管理器的工作,而不是开发人员的工作,但是正如以前讲过的,如果用例需要,HQL支持直接的大批量操作,用于更新,删除和插入)。
----------
2.延迟的默认抓取计划
Hibernate给所有的实体和集合默认一个延迟的抓取策略。这意味着Hibernate在默认情况下只加载你正在查询的对象。如例:如果查询Item对象(假设你通过它的标识符加载),则只有这个Item被加载到内存中去(Item有别的实体引用或集合):
load()操作之后,内存中并没有持久化的item对象。甚至加载Item的sql也没有执行。hibernate创建了一个看起来跟真的一样的代理;(注意:load()方法使用代理,get()不会使用代理的)。
----------
3.理解代理
代理是在运行时生成的占位符。每当hibernate返回实体类的实例时,它就检查是否可以返回一个代理来代替,并避免数据库命中。代理是当真实对象第一次被访问时触发其加载的占们符;
这个示例中的第三行触发了把Item获取到内存的SQL的执行。只要你只访问数据库标识符属性,就没有必要初始化代理。(注意:如果用直接的字段访问映射标识符属性,就不是这样了;Hibernate当时甚至不知道getId()方法的存在。如果调用它,代理就必须被初始化。)
如果需要Item只是用来创建一个引用,代理就很有用了,例如:
先加载两个对象:Item和User。Hibernate并没有命中数据库来完成这项工作,而是返回两个代理。这就是你所说的,因为你只需要Item和User来创建一个新的Bid。save(newBid)调用执行INSERT语句,通过Item和User的外键值保存BID表中的行---这是代理能够且必须提供的一切。前一个代码片段没有执行任何SELECT语句。
如果调用get()(而不是load()),就触发了一次数据库命中,且没有返回任何代理。get()操作始终命中数据库(如果实例没有处在持久化上下文中,并且如果没有任何透明的二极高速缓存是活动的),如果无法找到对象就返回null。
如果你调用任何不是标识符获取方法的方法,代理就被初始化,如果你通过集合的元素启动迭代,或者如果调用任何集合管理的操作,如size()和contains(),集合就被初始化。hibernate提供一项对大集合最有用的额外设置:它们可以被映射为extra延迟。例如,考虑Item的bids集合:
集合包装器现在比以前更智能了。如果调用size(),contains()或者isEmpty(),集合将不再初始化---查询数据库来获取必要的信息。如果它是一个Map或者List,containsKey()和get()操作也直接查询数据库。
----------
4.禁用代理生成
代理是个好东西,它们允许你只加载真正需要的数据,它们甚至让你创建对象之间的关联,而不用没有必要地命中数据库。有时候你会需要一种不同的计划---例如,你想要表达User对象应该永远被加载到内存中,并且不应该返回任何占位符来代替。
可以给一个特定的实体类禁用代理生成,通过xml映射元数据中的lazy="false"属性:
给实体禁用代理生成后,这个操作需要数据库命中:
----------
5.关联和集合的即时加载
你已经知道,hibernate默认情况下是延迟的。如果加载一个实体对象,所有被关联的实体和集合就都没有被初始化。一般来说,开发人员经常想要相反的结果:指定一个特定的实体关联或者集合应该始终被加载。你想要保证这个数据在内存中不用额外的数据库命中就可用。最重要的是,比如,即使Item实例处于脱管状态,你希望仍然可以访问Item的seller。必须定义这项抓取计划,这是你想要始终加载到内存中的一部分对象网络。
假设你始终需要Item的seller。在hibernate xml映射元数据中,映射从Item到User的关联为: lazy="false";
同样的"始终加载"保证可以被应用到集合---例如,Item的所有bids:
如果现在get()一个Item(或者强制初始化一个被代理的Item),seller对象和所有的bids都作为持久化实例被加载到你的持久化上下文中去: