为了避免一些情况下,关联关系所带来的无谓的性能开销。Hibernate引入了延迟加载的
概念。
如,示例中user对象在加载的时候,会同时读取其所关联的多个地址(address)对象,
对于需要对address进行操作的应用逻辑而言,关联数据的自动加载机制的确非常有效。
但是,如果我们只是想要获得user的性别(sex)属性,而不关心user的地址(address)
信息,那么自动加载address的特性就显得多余,并且造成了极大的性能浪费。为了获得user的性别属性,我们可能还要同时从数据库中读取数条无用的地址数据,这导致了大量无谓的系统开销。
延迟加载特性的出现,正是为了解决这个问题。
所谓延迟加载,就是在需要数据的时候,才真正执行数据加载操作。
对于我们这里的user对象的加载过程,也就意味着,加载user对象时只针对其本身的属性,
而当我们需要获取user对象所关联的address信息时(如执行user.getAddresses时),才真正从数据库中加载address数据并返回。
我们将前面一对多关系中的lazy属性修改为true,即指定了关联对象采用延迟加载:
尝试执行以下代码:
运行时抛出异常:
如果我们稍做调整,将session.close放在代码末尾,则不会发生这样的问题。
这意味着,只有我们实际加载user关联的address时,Hibernate才试图通过session从数据库中加载实际的数据集,而由于我们读取address之前已经关闭了session,所以报出session已关闭的错误。
这里有个问题,如果我们采用了延迟加载机制,但希望在一些情况下,实现非延迟加载时的功能,也就是说,我们希望在Session关闭后,依然允许操作user的addresses属性。如,为了向View层提供数据,我们必须提供一个完整的User对象,包含其所关联的address信息,而这个User对象必须在Session关闭之后仍然可以使用。
Hibernate.initialize方法可以通过强制加载关联对象实现这一功能:
为了实现透明化的延迟加载机制,hibernate进行了大量努力。其中包括JDK Collection接口的独立实现。
如果我们尝试用HashSet强行转化Hibernate返回的Set型对象:
就会在运行期得到一个java.lang.ClassCastException,实际上,此时返回的是一个Hibernate的特定Set实现“net.sf.hibernate.collection.Set”对象,而非传统意义上的JDK Set实现。
这也正是我们为什么在编写POJO时,必须用JDK Collection接口(如Set,Map),
而非特定的JDK Collection实现类(如HashSet、HashMap)申明Collection属性的原因。
回到前面TUser类的定义:
我们通过Set接口,申明了一个addresses属性,并创建了一个HashSet作为addresses的初始实例,以便我们创建TUser实例后,就可以为其添加关联的address对象:
此时,这里的addresses属性还是一个HashSet对象,其中包含了一个address对象的引用。那么,当调用session.save(user)时,Hibernate是如何处理这个HashSet型属性的呢?
通过Eclipse的Debug窗口,我们可以看到session.save方法执行前后user对象发
生的变化:
首先,由于insert操作,Hibernate获得数据库产生的id值(在我们的例子中,采用native方式的主键生成机制),并填充到user对象的id属性。这个变化比较容易理解。
另一方面,Hibernate使用了自己的Collection实现“net.sf.hibernate.collection.Set”对user中的HashSet型addresses属性进
行了替换,并用数据对其进行填充,保证新的addresses与原有的addresses包含同样的实体元素。
由于拥有自身的Collection实现,Hibernate就可以在Collection层从容的实现延迟加载特性。只有程序真正读取这个Collection时,才激发底层实际的数据库操作。
概念。
如,示例中user对象在加载的时候,会同时读取其所关联的多个地址(address)对象,
对于需要对address进行操作的应用逻辑而言,关联数据的自动加载机制的确非常有效。
但是,如果我们只是想要获得user的性别(sex)属性,而不关心user的地址(address)
信息,那么自动加载address的特性就显得多余,并且造成了极大的性能浪费。为了获得user的性别属性,我们可能还要同时从数据库中读取数条无用的地址数据,这导致了大量无谓的系统开销。
延迟加载特性的出现,正是为了解决这个问题。
所谓延迟加载,就是在需要数据的时候,才真正执行数据加载操作。
对于我们这里的user对象的加载过程,也就意味着,加载user对象时只针对其本身的属性,
而当我们需要获取user对象所关联的address信息时(如执行user.getAddresses时),才真正从数据库中加载address数据并返回。
我们将前面一对多关系中的lazy属性修改为true,即指定了关联对象采用延迟加载:
<hibernate-mapping>
<class
name="org.hibernate.sample.TUser"
table="t_user"
dynamic-update="true"
dynamic-insert="true"
>
<set
name="addresses"
table="t_address"
lazy="true"
inverse="false"
cascade="all"
sort="unsorted"
order-by="zipcode asc"
>
<key
column="user_id"
>
</key>
<one-to-many
class="org.hibernate.sample.TAddress"
/>
</set>
……
</class>
</hibernate-mapping>
尝试执行以下代码:
Criteria criteria = session.createCriteria(TUser.class);
criteria.add(Expression.eq("name","Erica"));
List userList = criteria.list();
TUser user =(TUser)userList.get(0);
System.out.println("User name => "+user.getName());
Set hset = user.getAddresses();
session.close();//关闭Session
TAddress addr = (TAddress)hset.toArray()[0];
System.out.println(addr.getAddress());
运行时抛出异常:
LazyInitializationException - Failed to lazily initialize a collection - no session or session was closed
如果我们稍做调整,将session.close放在代码末尾,则不会发生这样的问题。
这意味着,只有我们实际加载user关联的address时,Hibernate才试图通过session从数据库中加载实际的数据集,而由于我们读取address之前已经关闭了session,所以报出session已关闭的错误。
这里有个问题,如果我们采用了延迟加载机制,但希望在一些情况下,实现非延迟加载时的功能,也就是说,我们希望在Session关闭后,依然允许操作user的addresses属性。如,为了向View层提供数据,我们必须提供一个完整的User对象,包含其所关联的address信息,而这个User对象必须在Session关闭之后仍然可以使用。
Hibernate.initialize方法可以通过强制加载关联对象实现这一功能:
Hibernate.initialize(user.getAddresses());
session.close();
//通过Hibernate.initialize方法强制读取数据
//addresses对象即可脱离session进行操作
Set hset= user.getAddresses();
TAddress addr = (TAddress)hset.toArray()[0];
System.out.println(addr.getAddress());
为了实现透明化的延迟加载机制,hibernate进行了大量努力。其中包括JDK Collection接口的独立实现。
如果我们尝试用HashSet强行转化Hibernate返回的Set型对象:
Set hset = (HashSet)user.getAddresses();
就会在运行期得到一个java.lang.ClassCastException,实际上,此时返回的是一个Hibernate的特定Set实现“net.sf.hibernate.collection.Set”对象,而非传统意义上的JDK Set实现。
这也正是我们为什么在编写POJO时,必须用JDK Collection接口(如Set,Map),
而非特定的JDK Collection实现类(如HashSet、HashMap)申明Collection属性的原因。
回到前面TUser类的定义:
public class TUser implements Serializable {
……
private Set addresses = new HashSet();
……
}
我们通过Set接口,申明了一个addresses属性,并创建了一个HashSet作为addresses的初始实例,以便我们创建TUser实例后,就可以为其添加关联的address对象:
TUser user = new TUser();
TAddress addr = new TAddress();
addr.setAddress("Hongkong");
user.getAddresses().add(addr);
session.save(user);
此时,这里的addresses属性还是一个HashSet对象,其中包含了一个address对象的引用。那么,当调用session.save(user)时,Hibernate是如何处理这个HashSet型属性的呢?
通过Eclipse的Debug窗口,我们可以看到session.save方法执行前后user对象发
生的变化:
首先,由于insert操作,Hibernate获得数据库产生的id值(在我们的例子中,采用native方式的主键生成机制),并填充到user对象的id属性。这个变化比较容易理解。
另一方面,Hibernate使用了自己的Collection实现“net.sf.hibernate.collection.Set”对user中的HashSet型addresses属性进
行了替换,并用数据对其进行填充,保证新的addresses与原有的addresses包含同样的实体元素。
由于拥有自身的Collection实现,Hibernate就可以在Collection层从容的实现延迟加载特性。只有程序真正读取这个Collection时,才激发底层实际的数据库操作。