|||
一、Hibernate检索策略
1. 立即检索策略:检索一个对象时会把与之关联的对象检索出来。这种检索策略的不足之处在于:(1)需要频繁地访问数据库,影响检索性能;(2)往往多加载一些多余的对象,这会造成内存空间资源的浪费。
2. 延迟检索策略:避免加载多余的对象,防止空间资源浪费。
3. 迫切左外连接策略:利用SQL的外连接的查询功能,减少检索SQL语句的条数。
二、检索策略类型
1. 类级别:是否采用延迟检索或立即检索策略检索指定的类对象,如Session的load()和get()方法、Query的HQL语句检索类对象。
2. 关联级:是否采用延迟检索、立即检索或迫切左外连接检索策略检索对象及与之关联的其他对象。此外,延迟检索和立即检索策略都可以设置批量检索数量。
说明:类级别的延迟检索仅影响Session的load()方法,比如:
类级别采用延迟加载策略,当执行Session的load()方法时,Hibernate不会执行select语句,而是返回一个Customers 类的代理类(Hibernate采用CGLIB工具来生成持久化类的代理类)的实例,这个代理类具有以下特征:
(1) 由Hibernate运行时动态生成,它扩展了Customers 类,因此具有Customers 类的所有属性和方法。
(2) 当创建Hibernate代理类实例时,仅初始化了它的OID属性,其余属性都为null。
(3) 当应用程序第一次调用Customers代理类实例时,Hibernate会初始化代理类实例,在初始化过程中执行select语句,真正从数据库中加载Customers对象的所有数据,例如:
Customers c2 = (Customers)session.load(Customers.class, new Long(5));
c2.getName();//这是执行select语句
控制台打印出SQL语句为
Hibernate: select customers0_.ID as ID4_0_, customers0_.name as name4_0_ from customers customers0_ where customers0_.ID=?
当然也有例外,当应用程序访问代理类的实例的getId时,由于在创建代理类实例时OID已存在,因此Hibernate不会到数据库中查询。
// 不会执行select语句,返回id为5其它属性为null的代理类对象,
Customers c2 = (Customers)session.load(Customers.class, new Long(5));
c2.getId();//也不执行sql语句
控制台未打印出SQL语句。
通过以上分析,如果仅仅是为了获取对持久化对象的引用,可以采用延迟加载;如果是为了访问持久化类对象的属性,可以采用立即检索。
三、映射文件中用于设定检索策略的几个属性
1. <class>元素中的lazy:true(延迟检索,默认)、false(立即检索)。
2. <set>元素中的属性:(1)lazy:true(延迟检索,默认)、false(立即检索)和extra(增强延迟检索);(2)fetch:select(select查询语句,默认)、subselect(带子查询的select语句)和join(迫切左外连接检索)。
3. <many-to-one>元素中的属性:(1)lazy:proxy(延迟检索)、no-proxy(无代理延迟检索)和false(立即检索);(2)fetch:select(select查询语句,默认)和join(迫切左外连接检索)。
4. 设置批量检索数量属性batch-size:可选值为正整数,默认值为1。仅适用于关联级的延迟检索和立即检索,合理值为3-10。在<class>和<set>元素中设置此属性。
由于映射文件配置的检索策略固定的,无法满足各种应用逻辑的动态化需求,因此Hibernate提供了以编程方式显示设定检索策略。
四、Session的get方法与load方法在检索策略的区别
1. 采用类级别延迟加载,会影响Session的load()方法的各种运行时行为
(1)装载一个在数据库中不存在的Customers对象时,若从未访问该对象的方法(除id的setter和getter方法外),则执行load()方法不会抛出异常,直到访问该对象的方法时候才会抛出:
org.hibernate.ObjectNotFoundException。
(2)若处于Session实例的生命周期内通过load()方法返回的Customers代理类对象一直未被初始化(未执行select语句),则当该对象处于游离状态时,访问该对象的属性方法(除访问代理类实例的getId()方法外)时就会抛出异常:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
(3)org.hibernate.Hibernate.initialize(Object)用于显示初始化Session范围内的代理类实例。
Session hibernateSession = InitSessionUtil.openSession();
Customers customer = (Customers)hibernateSession.load(cls, id);
// 判断代理类实例是否已被初始化,若否则初始化代理类实例
if (!Hibernate.isInitialized(customer)) {
Hibernate.initialize(customer);
}
InitSessionUtil.closeSession();
这样,当customer对象处于游离状态时,仍然可以访问该对象了。
(4)访问代理类实例的getId()方法时,由于代理类的OID值已存在,无需访问数据库,因此不会初始化代理类实例。
2. 不管<class>元素的属性lazy设置为true或者是false,Session的get()方法及Query的list()方法在Customers类级别总是采用立即检索策略。如果在数据库中不存在该记录,则返回null。get()方法永远不会返回代理类实例。
Archiver|手机版|科学网 ( 京ICP备07017567号-12 )
GMT+8, 2024-5-19 02:01
Powered by ScienceNet.cn
Copyright © 2007- 中国科学报社