抓取策略(fetching strategy) 是指:当应用程序需要在(Hibernate实体对象图的)关联关系间进行导航的时候, Hibernate如何获取关联对象的策略。抓取策略可以在O/R映射的元数据中声明,也可以在特定的HQL 或条件查询(Criteria Query)中重载声明。
- <span style="font-size: large;">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!</span>
除此之外,通过对关联映射指定lazy="false",我们也可以使用非延迟的集合或关联。但是, 对绝大部分集合来说,更推荐使用延迟方式抓取数据。如果在你的对象模型中定义了太多的非延迟关联,Hibernate最终几乎需要在每个事务中载入整个数据库到内存中!
但是,另一方面,在一些特殊的事务中,我们也经常需要使用到连接抓取(它本身上就是非延迟的),以代替查询抓取。 下面我们将会很快明白如何具体的定制Hibernate中的抓取策略。在Hibernate3中,具体选择哪种抓取策略的机制是和选择 单值关联或集合关联相一致的。
2. 调整抓取策略(Tuning fetch strategies)
查询抓取(默认的)在N+1查询的情况下是极其脆弱的,因此我们可能会要求在映射文档中定义使用连接抓取:
- <span style="font-size: large;"><set name="permissions" fetch="join">
- <key column="userId"/>
- <one-to-many class="Permission"/>
- </set>
- <many-to-one name="mother" class="Cat" fetch="join"/> </span>
条件查询,使用了subselect抓取的HQL查询
不管你使用哪种抓取策略,定义为非延迟的类图会被保证一定装载入内存。注意这可能意味着在一条HQL查询后紧跟着一系列的查询。
通常情况下,我们并不使用映射文档进行抓取策略的定制。更多的是,保持其默认值,然后在特定的事务中, 使用HQL的左连接抓取(left join fetch) 对其进行重载。这将通知 Hibernate在第一次查询中使用外部关联(outer join),直接得到其关联数据。 在条件查询 API中,应该调用 setFetchMode(FetchMode.JOIN)语句。
也许你喜欢仅仅通过条件查询,就可以改变get() 或 load()语句中的数据抓取策略。例如:
- <span style="font-size: large;">User user = (User) session.createCriteria(User.class) .
- setFetchMode("permissions", FetchMode.JOIN).
- add( Restrictions.idEq(userId) ).uniqueResult(); </span>
(这就是其他ORM解决方案的“抓取计划(fetch plan)”在Hibernate中的等价物。)截然不同的一种避免N+1次查询的方法是,使用二级缓存。
- <span style="font-size: large;"><?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE hibernate-mapping PUBLIC
- "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
- "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
- <hibernate-mapping>
- <class name="com.javacrazyer.domain.Product" table="product">
- <id name="id">
- <generator class="native"/>
- </id>
- <version name="version"/>
- <property name="name"/>
- <property name="unitCost" column="unit_cost"/>
- <property name="pubTime" column="pub_time"/>
- <!-- 映射多对一 -->
- <many-to-one name="cate" column="cate_id" fetch="join"/>
- </class>
- </hibernate-mapping></span>
- <span style="font-size: large;">select tab1.xx,tab1.yy,tab2.aa,tab2.cc
- from product tab1
- inner join category tab2
- on tab1.cate_id=tab2.id
- where product_id=?</span>
- <span style="font-size: large;"><?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE hibernate-mapping PUBLIC
- "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
- "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
- <hibernate-mapping>
- <class name="com.javacrazyer.domain.Category" table="category" batch-size="10">
- <id name="id">
- <generator class="native"/>
- </id>
- <version name="version"/>
- <property name="name"/>
- <property name="description"/>
- <!-- 映射集合属性 -->
- <bag name="productList" inverse="true" fetch="subselect">
- <key column="cate_id"/>
- <!-- 映射一对多 -->
- <one-to-many class="com.javacrazyer.domain.Product"/>
- </bag>
- </class>
- </hibernate-mapping></span>