`
zhiweiofli
  • 浏览: 515082 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Hibernate学习手记(8) - 抓取策略

阅读更多

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

连接抓取(Joinfetching)-Hibernate通过在SELECT语句使用OUTERJOIN(外连接)来获得对象的关联实例或者关联集合

查询抓取(Selectfetching)-另外发送一条SELECT语句抓取当前对象的关联实体或集合。这也就是通过外键的方式来执行数据库的查询。除非你显式的指定lazy="false"禁止延迟抓取(lazyfetching),否则只有当你真正访问关联关系的时候,才会执行第二条select语句

子查询抓取(Subselectfetching)-另外发送一条SELECT语句抓取在前面查询到(或者抓取到)的所有实体对象的关联集合。与查询抓取的区别在于它所用的SELECT语句的方式为子查询,而不是通过外连接。除非你显式的指定lazy="false"禁止延迟抓取(lazyfetching),否则只有当你真正访问关联关系的时候,才会执行第二条select语句

批量抓取(Batchfetching)-对查询抓取的优化方案,通过指定一个主键或外键列表,Hibernate使用单条SELECT语句获取一批对象实例或集合

抓取关联对象的时机上有以下几点:

立即抓取,延迟代理抓取,延迟属性抓取,延迟集合抓取

默认对对象关联是采取延迟加载,普通属性采用立即抓取。

对于延迟加载,需要注意的是,对于延迟对象的使用必须在Session关闭之前进行,否则会报LazyInitalizationException

对于集合类型的关联,在默认情况下会使用延迟集合加载的抓取时机,而对于返回单值类型的关联在默认情况下会使用延迟代理抓取的抓取时机。

A.应用于对象关联实例(默认是延迟加载,默认fetch="select"

<many-to-onename="......"lazy="true/false"fetch="select"/>

B.应用于对象关联集合(默认是延迟加载,默认fetch="select",默认batch-size="1"

<classname="......"table="......"batch-size="1">

......

<setname="......"lazy="true/false"fetch="select">

......

</set>

</class>

(注意:设置fetch“join”,会导致lazy失效;抓取粒度batch-size并不是越多越好,其值超过50之后,对性能并没有多大改善反而无谓地消耗内存,一般来说,被关联表数据较少则可设置小一点,3~20,如果比较大则可以设到30~50

以下CustomerOrder的一个双向一对多例子来使用四种抓取策略看看他们的不同之处;

Customer:

lpublicclassCustomer{

lprivatelongid;

lprivateStringname;

lprivateSet<Order>orders;

l//getter/setter

l}

Order

lpublicclassOrder{

lprivatelongid;

lprivateStringname;

lprivateCustomercustomer;

l//getter/setter

l}

Order的映射文件是不变的:

l<hibernate-mapping>

l<classname="Order"table="Order_Table">

l<idname="id">

l<generatorclass="native"/>

l</id>

l<propertyname="name"length="20"column="Order_Name"/>

l<many-to-onename="customer"class="Customer"lazy="proxy"fetch="select"

lcolumn="Cus_ID"cascade="save-update"/>

l</class>

l</hibernate-mapping>

连接抓取(Joinfetching

连接抓取,使用连接抓取可以将原本需要查询两次(或多次)表的多次查询整合到只需要一次查询即可完成举个例子,我们在初始化一个含有一对多关系的CustomerOrder的时候,会先查询Customer,找到需要的Customer然后再根据Customer.idOrder表中查询将Order集合初始化,那么在此完成初始化则需要发送至少两条SQL语句,而如果使用join查询的话,其会根据需要查询的Customer.id,Customer表与Order表连接起来进行查询,仅仅一条SQL语句就可以将需要的数据全部查询回来;使用连接抓取的配置文件:

l<hibernate-mappingpackage="com.purking.strategys.endUpOne">

l<classname="Customer"table="Customer_Table"lazy="true">

l<idname="id">

l<generatorclass="native"/>

l</id>

l<propertyname="name"length="20"column="Cus_Name"/>

l<setname="orders"

linverse="true"

lfetch="join"//----Here

l<!--这里关闭懒加载是为了试验明显-->

llazy="false">

l<keycolumn="Cus_ID"/>

l<one-to-manyclass="Order"/>

l</set>

l</class>

l</hibernate-mapping>

我们使用如此查询语句:

Customerc1=(Customer)session.get(Customer.class,11l);

c1.getOrders().size();

Hibernate发出的SQL语句为:

lselect

lcustomer0_.idasid0_1_,

lcustomer0_.Cus_NameasCus2_0_1_,

lorders1_.Cus_IDasCus3_3_,

lorders1_.idasid3_,

lorders1_.idasid1_0_,

lorders1_.Order_NameasOrder2_1_0_,

lorders1_.Cus_IDasCus3_1_0_

lfrom

lCustomer_Tablecustomer0_

lleftouterjoin

lOrder_Tableorders1_

loncustomer0_.id=orders1_.Cus_ID

lwhere

lcustomer0_.id=?

在此,Hibernate使用了leftouterjoin连接两个表以一条SQL语句将Order集合给初始化了;


查询抓取(Selectfetching

查询抓取,这种策略是在集合抓取的时候的默认策略,即如果集合需要初始化,那么会重新发出一条SQL语句进行查询;这是集合默认的抓取策略,也就是我们常会出现N+1次查询的查询策略;

配置文件:

l<hibernate-mappingpackage="com.purking.strategys.endUpOne">

l<classname="Customer"table="Customer_Table"lazy="true">

l<idname="id">

l<generatorclass="native"/>

l</id>

l<propertyname="name"length="20"column="Cus_Name"/>

l<setname="orders"

linverse="true"

lfetch="select">

l<keycolumn="Cus_ID"/>

l<one-to-manyclass="Order"/>

l</set>

l</class>

l</hibernate-mapping>

查询语句不变,看看Hibernate发出的SQL语句:

lHibernate:

lselect

lcustomer0_.idasid0_0_,

lcustomer0_.Cus_NameasCus2_0_0_

lfrom

lCustomer_Tablecustomer0_

lwhere

lcustomer0_.id=?

lHibernate:

lselect

lorders0_.Cus_IDasCus3_1_,

lorders0_.idasid1_,

lorders0_.idasid1_0_,

lorders0_.Order_NameasOrder2_1_0_,

lorders0_.Cus_IDasCus3_1_0_

lfrom

lOrder_Tableorders0_

lwhere

lorders0_.Cus_ID=?

这就是,重新发出一条SQL语句,初始化了Orders集合;

子查询抓取(Subselectfetching

子查询抓取,另外发送一条SELECT语句抓取在前面查询到(或者抓取到)的所有实体对象的关联集合.这个理解起来有点糊涂,举个例子:如果你使用Query查询出了4Customer实体,由于开启了懒加载,那么他们的Orders都没有被初始化,那么我现在手初始化一个CustomerOrders,此时由于我选的是Subselectfetching策略,所以Hibernate会将前面查询到的实体对象(4Customer)的关联集合(<setname="orders"fetch="subselect"/>)使用一条Select语句一次性抓取回来,这样减少了与数据库的交互次数,一次将每个对象的集合都给初始化了

[他是如何这么智能的呢?原来,他是将上一次查询的SQL语句作为这一次查询的SQL语句的where子查询,所以上次查询到几个对象,那么这次就初始化几个对象的集合-----正因为如此,所以subselect只在<set>集合中出现];

配置文件:

l<hibernate-mappingpackage="com.purking.strategys.endUpOne">

l<classname="Customer"table="Customer_Table"lazy="true">

l<idname="id">

l<generatorclass="native"/>

l</id>

l<propertyname="name"length="20"column="Cus_Name"/>

l<setname="orders"

linverse="true"

lfetch="subselect"

llazy="true">

l<keycolumn="Cus_ID"/>

l<one-to-manyclass="Order"/>

l</set>

l</class>

l</hibernate-mapping>

测试的语句有变化:

Listresults=session.createQuery("FromCustomercwherec.idin(11,14,17,20)").list();

//这里的四个id是我数据库中已经准备好的数据

Customerc0=(Customer)results.get(0);

c0.getOrders().size();

这个时候再来看看Hibernate发出了什么样的SQL语句:

lHibernate:

lselect

lcustomer0_.idasid0_,

lcustomer0_.Cus_NameasCus2_0_

lfrom

lCustomer_Tablecustomer0_

lwhere

lcustomer0_.idin(

l11,14,17,20

l)

lHibernate:

lselect

lorders0_.Cus_IDasCus3_1_,

lorders0_.idasid1_,

lorders0_.idasid1_0_,

lorders0_.Order_NameasOrder2_1_0_,

lorders0_.Cus_IDasCus3_1_0_

lfrom

lOrder_Tableorders0_

lwhere

lorders0_.Cus_IDin(

lselect

lcustomer0_.id

lfrom

lCustomer_Tablecustomer0_

lwhere

lcustomer0_.idin(

l11,14,17,20

l)

l)

是不是发出的SQL语句形式与这个抓取策略的名字一样?Hibernate的命名很清晰的;

; fon

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics