One to Many
1: Oneto Many,默认是采用延迟加载的(lazy=true),因为很多情况下,当我们获取到One的一方后,Many的一方并非立即需要的,当我们经常只需要One的一方的时候,延迟加载就很有意义了.
2:Hibernate 中的延迟加载(lazy loading),当我们在程序中获取到了一的一方,但是不需要多的一方,那么使用延迟加载就是非常适合的.
3:当我们One和Many都要同时需要的时候,我们可以将One的一方的hbm.xml中用于保存Many的Set元素的lazy改为false
One to Many的延迟加载分析
当我们通过通过Session.get()查询到One的一方后,其中用于保存Many的Set其实是一个代理,其类型为org.hibernate.collection.PersistentSet, PersistentSet 实现了Java.util.Set接口,并内置了一个用于存放实际数据的HashSet,初始时为空,当真正要访问这个集合的元素时,如调用Java.util.Set#iterator()#toArray() #containsAll(),#toString,#equal()#hashCode等要真正访问集合的元素的方法的时候,程序才主要发出SQL语句,真正加载Many一方,并且一次读取完Many一方的所有元素.
PersistentSet 实现了Java.util.Set接口,并内置了一个HashSet实例,PersistentSet代理了Java.util.Set的#iterator()#toArray() #containsAll(),#toString等方法: 而代理的过程很简单:
若要访问到Many的数据,则先加载Many的所有元素,再简单调用被代理的方法!
这里也是应用到"代理模式"的思想..框架里,这个模式很常用......
在Hibernate里,延迟加载基本是用"代理模式"实现的!
如:
public class PersistentSet extendsAbstractPersistentCollection implements java.util.Set { 。。。。。。。。。。。。。。 publicString toString() { read(); //先加载Many一方 return set.toString(); } 。。。。。。。。。。。 }
再看看read()方法:
public abstract classAbstractPersistentCollection implementsSerializable, PersistentCollection { ………………………………… /** * Called by any read-only method of the collection interface */ protected final void read() { initialize(false); } protectedfinal void initialize(boolean writing) { if (!initialized) { if(initializing) { throw new LazyInitializationException("illegal access toloading collection"); } //检查与数据库的连接 throwLazyInitializationExceptionIfNotConnected(); //这里真正加载Many session.initializeCollection(this, writing); } } ……………………………………. }
Order <- many-to-one ->Customer
在Many.hbm.xml中
<many-to-one name="customer"class="beans.Customer" column="customer_id" lazy="false">
</many-to-one>
1. lazy="false"
false:取消懒加载策略,即在加载对象的同时,发出查询语句,加载其关联对象
意为: 在Many一方中查询出一个对象之后,同时也要查出One的一方. One的一方的类型为One的实际类型(非代理类,这里是Customer)
2. lazy="proxy" //这是默认值
proxy:这是hibernate对单端关联的默认懒加载策略,即只有在调用到其关联对象的方法的时候才真正发出查询语句查询其对象数据,其关联对象是代理类
意为: 在Many一方中查询出一个对象之后,One的一方用代理类代替(Customer_$$_javassist_1),这个代理只包含One的一方的ID,而这个ID是在Many这一方的标识One的外键,目的是为了延迟初始化One的一方.,当真正访问One的一方时,才正式初始化它.所以最终取得的是代理类(非实际的Customer类型,初始化前只含Customer的ID)
3.lazy="no-proxy"
不常用…,那个” 编译时字节码增强”都不知道是什么了的…本人没有什么增强工具的情况,效果和proxy一样的,也是用代理类 Customer_$$_javassist_1 来代理了Customer对象,用反射分析到两种情况下的代理类的字段,方法都一样的…这个不管了..
网上的分析:
Child <- many-to-one ->Parent
class Child {
private Parent parent;
public Parent getParent (){
return this.parent;//访问了实例变量
}
}
class Parent {
private String name;
public String getName(){
return this.name;//访问了实例变量
}
public void f(){
System.out.println("invokeing f()");//没有访问实例变量
}
}
如果 many-to-one 的lazy设为proxy,当child.getParent().getName()或child.getParent().f()时,parent都会被抓取,若设为no-proxy,调用child.getParent().f()时,parent是不会被抓取的,同时这种方式需要编译时字节码增强,否则和proxy没区别。
--------------------------------------------------------------