美文网首页
hibernate(三)[延迟加载]

hibernate(三)[延迟加载]

作者: FTOLsXD | 来源:发表于2017-02-17 21:52 被阅读58次

这就是 PersistentSet 集合跟据 owner 属性去抓取特定 Address 记录的 SQL 语句。此时可以从 Eclipse 的 Variables 窗口看到图 3 所示的输出:

图 3. 已加载的集合属性值

从图 3 可以看出,此时的 addresses 属性已经被初始化了,集合里包含了 2 个 Address 对象,这正是 Person 实体所关联的两个 Address 对象。

通过上面介绍可以看出,Hibernate 对于 Set 属性延迟加载关键就在于 PersistentSet 实现类。在延迟加载时,开始 PersistentSet 集合里并不持有任何元素。但 PersistentSet 会持有一个 Hibernate Session,它可以保证当程序需要访问该集合时“立即”去加载数据记录,并装入集合元素。

与 PersistentSet 实现类类似的是,Hibernate 还提供了 PersistentList、PersistentMap、PersistentSortedMap、PersistentSortedSet 等实现类,它们的功能与 PersistentSet 的功能大致类似。

熟悉 Hibernate 集合属性读者应该记得:Hibernate 要求声明集合属性只能用 Set、List、Map、SortedSet、SortedMap 等接口,而不能用 HashSet、ArrayList、HashMap、TreeSet、TreeMap 等实现类,其原因就是因为 Hibernate 需要对集合属性进行延迟加载,而 Hibernate 的延迟加载是依靠 PersistentSet、PersistentList、PersistentMap、PersistentSortedMap、PersistentSortedSet 来完成的——也就是说,Hibernate 底层需要使用自己的集合实现类来完成延迟加载,因此它要求开发者必须用集合接口、而不是集合实现类来声明集合属性。

Hibernate 对集合属性默认采用延迟加载,在某些特殊的情况下,为 <set.../>、<list.../>、<map.../> 等元素设置 lazy="false"属性来取消延迟加载。


关联实体的延迟加载

默认情况下,Hibernate 也会采用延迟加载来加载关联实体,不管是一对多关联、还是一对一关联、多对多关联,Hibernate 默认都会采用延迟加载。
对于关联实体,可以将其分为两种情况:

<li>关联实体是多个实体时(包括一对多、多对多):此时关联实体将以集合的形式存在,Hibernate 将使用 PersistentSet、PersistentList、PersistentMap、PersistentSortedMap、PersistentSortedSet 等集合来管理延迟加载的实体。这就是前面所介绍的情形。
<li>关联实体是单个实体时(包括一对一、多对一):当 Hibernate 加载某个实体时,延迟的关联实体将是一个动态生成代理对象。

当关联实体是单个实体时,也就是使用 <many-to-one.../> 或 <one-to-one.../> 映射关联实体的情形,这两个元素也可通过 lazy 属性来指定延迟加载。

下面例子把 Address 类也映射成持久化类,此时 Address 类也变成实体类,Person 实体与 Address 实体形成一对多的双向关联。此时的映射文件代码如下:

<?xml version="1.0" encoding="GBK"?> 
 <!-- 指定 Hibernate 的 DTD 信息 --> 
 <!DOCTYPE hibernate-mapping PUBLIC 
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 
 <hibernate-mapping package="org.crazyit.app.domain"> 
 <!-- 映射 Person 持久化类 --> 
 <class name="Person" table="person_inf"> 
 <!-- 映射标识属性 id --> 
 <id name="id" column="person_id"> 
 <!-- 定义主键生成器策略 --> 
 <generator class="identity"/> 
 </id> 
 <!-- 用于映射普通属性 --> 
 <property name="name" type="string"/> 
 <property name="age" type="int"/> 
 <!-- 映射集合属性,集合元素是其他持久化实体
没有指定 cascade 属性,指定不控制关联关系 --> 
 <set name="addresses" inverse="true"> 
 <!-- 指定关联的外键列 --> 
 <key column="person_id"/> 
 <!-- 用以映射到关联类属性 --> 
 <one-to-many class="Address"/> 
 </set> 
 </class> 

 <!-- 映射 Address 持久化类 --> 
 <class name="Address" table="address_inf"> 
 <!-- 映射标识属性 addressId --> 
 <id name="addressId" column="address_id"> 
 <!-- 指定主键生成器策略 --> 
 <generator class="identity"/> 
 </id> 
 <!-- 映射普通属性 detail --> 
 <property name="detail"/> 
 <!-- 映射普通属性 zip --> 
 <property name="zip"/> 
 <!-- 必须指定列名为 person_id, 
与关联实体中 key 元素的 column 属性值相同 --> 
 <many-to-one name="person" class="Person"
 column="person_id" not-null="true"/> 
 </class> 
 </hibernate-mapping> 

接下来程序通过如下代码片段来加载 ID 为 1 的 Person 实体:

// 打开上下文相关的 Session 
 Session session = sf.getCurrentSession(); 
 Transaction tx = session.beginTransaction(); 
 Address address = (Address) session.get(Address.class , 1); //<1> 
 System.out.println(address.getDetail()); 

为了看到 Hibernate 加载 Address 实体时对其关联实体的处理,我们在 <1>号代码处设置一个断点,在 Eclipse 中进行 Debug,此时可以看到 Eclipse 的 Console 窗口输出如下 SQL 语句:

select 
        address0_.address_id as address1_1_0_, 
        address0_.detail as detail1_0_, 
        address0_.zip as zip1_0_, 
        address0_.person_id as person4_1_0_ 
    from 
        address_inf address0_ 
    where 
        address0_.address_id=? 

从这条 SQL 语句不难看出,Hibernate 加载 Address 实体对应的数据表抓取记录,并未从 Person 实体对应的数据表中抓取记录,这是延迟加载发挥了作用。
从 Eclipse 的 Variables 窗口看到如图 4 所示的输出:

图 4. 延迟加载的实体

从图 4 可以清楚地看到,此时 Address 实体所关联的 Person 实体并不是 Person 对象,而是一个 Person_$$_javassist_0 类的实例,这个类是 Hibernate 使用 Javassist 项目动态生成的代理类——当 Hibernate 延迟加载关联实体时,将会采用 Javassist 生成一个动态代理对象,这个代理对象将负责代理“暂未加载”的关联实体。

只要应用程序需要使用“暂未加载”的关联实体,Person_$$_javassist_0 代理对象会负责去加载真正的关联实体,并返回实际的关联实体——这就是最典型的代理模式。
单击图 4 所示 Variables 窗口中的 person 属性(也就是在调试模式下强行使用 person 属性),此时看到 Eclipse 的 Console 窗口输出如下的 SQL 语句:

select 
        person0_.person_id as person1_0_0_, 
        person0_.name as name0_0_, 
        person0_.age as age0_0_ 
    from 
        person_inf person0_ 
    where 
        person0_.person_id=? 

上面 SQL 语句就是去抓取“延迟加载”的关联实体的语句。此时可以看到 Variables 窗口输出图 5 所示的结果:

图 5. 已加载的实体

Hibernate 采用“延迟加载”管理关联实体的模式,其实就在加载主实体时,并未真正去抓取关联实体对应数据,而只是动态地生成一个对象作为关联实体的代理。当应用程序真正需要使用关联实体时,代理对象会负责从底层数据库抓取记录,并初始化真正的关联实体。

在 Hibernate 的延迟加载中,客户端程序开始获取的只是一个动态生成的代理对象,而真正的实体则委托给代理对象来管理——这就是典型的代理模式。

相关文章

网友评论

      本文标题:hibernate(三)[延迟加载]

      本文链接:https://www.haomeiwen.com/subject/rgalwttx.html