您的位置:首页 > 其它

hibernate加载策略

2011-08-26 16:18 148 查看
抓取策略(Fetching strategies)

抓取策略(fetching strategy)是指:当应用程序使用关联对象的时候,Hibernate如何获取关联对象的策略。

抓取策略可以在O/R映射的元数据中声明,也可以在特定的HQL或条件查询(Criteria Query)中重载声明。

在映射文档中定义的抓取策略将会对以下列表条目产生影响:

通过get()或load()方法取得数据。

只有在关联之间进行导航时,才会隐式的取得数据。

注意:对HQL 无效

Hibernate3 定义了如下几种抓取策略:

连接抓取(fetch="join") - Hibernate通过 在SELECT语句使用OUTER JOIN(外连接)来获得对象的关联实例或者关联集合。

查询抓取(fetch="select") - 另外发送一条 SELECT 语句抓取当前对象的关联实体或集合。

除非你显式的指定lazy="false"禁止延迟抓取(lazy fetching),否则只有当你真正访问关联关系的时候,才会执行第二条select语句。

子查询抓取(fetch="subselect") - 另外发送一条SELECT 语句抓取在前面查询到(或者抓取到)的所有实体对象的关联集合。

除非你显式的指定lazy="false" 禁止延迟抓取(lazy fetching),否则只有当你真正访问关联关系的时候,才会执行第二条select语句。

批量抓取(Batch fetching) - 对查询抓取的优化方案, 通过指定一个主键或外键列表,Hibernate使用单条SELECT语句获取一批对象实例或集合。 <property name="default_batch_fetch_size">10</property>

hibernate抓取策略(单端代理的批量抓取)

(一)保持默认,同fetch="select",如:

<many-to-one name="classes" column="classesid" fetch="select"/>

fetch="select",另外发送一条select语句抓取当前对象关联实体或集合

(二)设置fetch="join",如:

<many-to-one name="classes" column="classesid" fetch="join"/>

fetch="join",hibernate会通过select语句使用外连接来加载其关联实体或集合,此时lazy会失效



hibernate抓取策略(集合代理的批量抓取)


Query query = session.createQuery("from Classes where id in(1,2,3)");

List<Classes> list = query.list();

for(Classes c : list){

for(Iterator i = c.getStudents().iterator();i.hasNext();){

Student s = (Student) i.next();

System.out.println("姓名:"+s.getName()+"班级:"+c.getClassName());

}

}

(一)保持默认,同fetch="select",如:

<set name="students" inverse="true" cascade="all" fetch="select">

fetch="select",另外发送一条select语句抓取当前对象关联实体或集合

(二)设置fetch="join",如:

<set name="students" inverse="true" cascade="all" fetch="join">

fetch="join",hibernate会通过select语句使用外连接来加载其关联实体或集合

此时lazy会失效

(三)设置fetch="subselect",如:

<set name="students" inverse="true" cascade="all" fetch="subselect">

fetch="subselect",另外发送一条select语句抓取在前面查询到的所有实体对象的关联集合

批量抓取

批量抓取是延迟查询抓取的优化方案,你可以在两种批量抓取方案之间进行选择:在类级别和集合级别。

batch-szie可以在类级别的批量加载实体

<class name="Classes" table="t_classes"
batch-size="3"> 类上的批量抓取 from Student s where id in(1,11,21)

batch-szie可以在集合级别的批量加载实体

<set name="students" inverse="true" cascade="all"
batch-size="5"> 集合上的批量抓取 from Classes c where id in(1,2,3)

类/实体级别的批量抓取很容易理解。假设你在运行时将需要面对下面的问题:你在一个Session中载入了25个Cat实例,每个Cat实例都拥有一个引用成员owner,其指向Person,而Person类是代理,同时lazy="true"。 如果你必须遍历整个cats集合,对每个元素调用getOwner()方法,Hibernate将会默认的执行25次SELECT查询, 得到其owner的代理对象。这时,你可以通过在映射文件的Person属性,显式声明batch-size,改变其行为:

<class name="Person" batch-size="10">...</class>

随之,Hibernate将只需要执行三次查询,分别为10、10、 5。

你也可以在集合级别定义批量抓取。例如,如果每个Person都拥有一个延迟载入的Cats集合, 现在,Sesssion中载入了10个person对象,遍历person集合将会引起10次SELECT查询, 每次查询都会调用getCats()方法。如果你在Person的映射定义部分,允许对cats批量抓取, 那么,Hibernate将可以预先抓取整个集合。请看例子:

<class name="Person">

<set name="cats" batch-size="3">

...

</set>

</class>

如果整个的batch-size是3,那么Hibernate将会分四次执行SELECT查询, 按照3、3、3、1的大小分别载入数据。

这里的每次载入的数据量还具体依赖于当前Session中未实例化集合的个数。

延迟抓取要注意的问题。在一个打开的Hibernate session上下文之外调用延迟集合会导致一次意外。

比如:

s = sessions.openSession();

Transaction tx = s.beginTransaction();

User u = (User) s.createQuery("from User u where u.name=:userName").setString("userName", userName).uniqueResult();

Map permissions = u.getPermissions();

tx.commit();

s.close();

Integer accessLevel = (Integer) permissions.get("accounts"); // Error!

在Session关闭后,permessions集合将是未实例化的、不再可用,因此无法正常载入其状态。

Hibernate对脱管对象不支持延迟实例化. 这里的修改方法是:将permissions读取数据的代码 移到tx.commit()之前。

通常情况下,我们并不使用映射文档进行抓取策略的定制。更多的是,保持其默认值,然后在特定的事务中,使用HQL的左连接抓取(left join fetch) 对其进行重载。这将通知 Hibernate在第一次查询中使用外部关联(outer join),直接得到其关联数据。

在条件查询 API中,应该调用 setFetchMode(FetchMode.JOIN)语句。

例如:

User user = (User) session.createCriteria(User.class)

.setFetchMode("permissions",
FetchMode.JOIN)

.add( Restrictions.idEq(userId) )

.uniqueResult();

截然不同的一种避免N+1次查询的方法是,使用二级缓存

使用子查询抓取(Using subselect fetching)

假若一个延迟集合或单值代理需要抓取,Hibernate会使用一个subselect重新运行原来的查询,一次性读入所有的实例。这和批量抓取的实现方法是一样的,不会有破碎的加载。

关联(查询引用实体),批量(类批量抓取,集合批量抓取)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: