数据库中得到一个对象的两种方式,一种是session.get()方法;另一种就是通过session.load()方法。load支持懒加载机制(等到使用非主键时才去读库),而get方法不支持懒加载。
一、懒加载机制
只有真正使用实体对象时,才发出Sql语句加载它。所谓真正使用实体对象时是指调用了对象的方法,其中不包括getId,getClass方法。
情况1:当使用load方法加载实体时,会看映射文件中配置该类的class标签上的lazy属性,lazy=true才实现懒加载
情况2:当使用load方法加载关联对象时,会看映射文件中配置该关联对象的标签上的lazy属性,然后再决定是否实现懒加载
注:
使用懒加载的前提是这个对象一定在数据库中存在,否则会抛出异常
懒加载生命周期与session有关,lazy加载必须依赖于session一直开启,session关闭懒加载就失效,抛LazyInistialzationException
二、优点
避免无谓的性能开销
三、懒加载配置
<class>标签(lazy=true/false)
只对普通属性的延迟加载有效,不包括集合和其他类属性
<property>标签上,取值可以为true、false(需要增强类)
<set><List>集合标签(true/false/extra)
如果设置为true,那么就会在该集合被加载时发出SQL语句
如果设置为false,那么在发出查询普通属性sql时就会随后发出集合的查询语句
如果设置为extra,与设置为true类似,但相对智能,建议在实际工作中选用extra
<one-to-one><many-to-one>单端关联标签(proxy/false/noproxy)
如果取值为proxy,那么就会在类对象被加载时发出SQL语句
如果设置为false,那么在发出查询普通属性sql时就会随后发出类对象的查询语句。
四、加载过程
1、load
User user=session.load(User.class,uid);
(1)先查一下session缓存,看看该id对应的对象是否存在
(2)缓存中没有这个对象,并且设置了延迟加载,就创建个代理;否则直接访问数据库,查到记录返回
因为延迟加载需要代理来执行所以就创建了个代理,这个并没有去数据库交互查询。当你使用这个对象user.getName()或get()方法时候,此时才会触发sql语句。这时hibernate就去查询二级缓存和数据库,数据库没有这条数据就抛出异常ObjectNotFoundException
如果只是获取他的id,则不会加载,不会出现select语句,只有获取他和本身之外才会进行查找,否则访问的只是它的代理对象而已。 因为load后会在hibernate的一级缓存里存放一个map对象,该map的key就是uid的值,但是当你getId()时,它会去一级缓存中那map的key值,而不去执行数据库查询。所以不会报任何错误,不会执行任何数据库操作。
2、get
hibernate会确认一下该id对应的数据是否存在,首先在session缓存中查找,然后在二级缓存中查找,还没有就查数据库,数据库中没有就返回null。
例如:User user=session.load(User.class,uid);
(1) get方法首先查询session缓存
(2) get方法如果在session缓存中找到了该id对应的对象,如果刚好该对象前面是被代理过的,如被load方法使用过,或者被其他关联对象延迟加载过,那么返回的还是原先的代理对象,而不是实体类对象。如果该代理对象还没有加载实体数据,那么它会查询二级缓存或者数据库来加载数据,但是返回的还是代理对象,只不过已经加载了实体数据。
(3) get方法如果在session缓存中找到了该id对应的对象,并且不是被延迟加载的代理对象,或者在session缓存中没有找到该对象,则查找二级缓存,再没有就查找数据库,返回的对象为实体对象,如果都没有找到,则返回null
五、实例
用户类和部门类是多对一的关系
数据库:用户表(did是部门id)
1、load
用户类不会立即载入,当我们取用户的姓名时,用户类载入了,但部门类仍然未载入。直到取部门的相关属性时,部门类才载入
(1)创建对象时不发出语句,真正使用对象时发出sql语句(用户ID为1时)
注:load方法返回User的代理对象
(2)获取主键ID时不发出语句,只有获取非主键属性时才发出语句(用户ID为1时,查询用户信息,获取用户姓名)
获取主键ID
获取用户姓名(发送sql语句)
原因:创建代理对象时,对象只保存实体的主键ID,所以在获取主键属性时,没有查询数据库,固不会有sql语句,即使查询的id在数据库中不存在,也不会报错
不报错的原因是因为id保存在缓存中,查询其他属性会报错
(3)查询时对象不存在,报错ObjectNotFoundException
用户id为3时,查询用户姓名:
2、get
用户类会马上被载入,而部门类仍然是懒加载。只有用到部门类的时候,才会被载入部门实体。所以说用了get并不是说懒加载完全失效,直接操作的类会直接载入,引用类仍然按照配置懒加载。
(1)创建对象时就发出语句 (用户id为1,查询用户信息和部门信息)
@Test
public void testGet(){
Session session=null;
try {
session=HibernateUtils.getSession();
session.beginTransaction();
long uid=1;
System.out.println("——————————查询方式get————————————");
System.out.println("查询前——创建对象");
Object object=session.get(User.class,uid);
System.out.println("查询后——查看对象");
User user=(User) object;
System.out.println(user);
System.out.println("查询后——使用对象");
System.out.println(user.getDepartment().getDid());
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
}
}
结果:
注:返回真实的User对象。get方法返回的是实体对象本身,而load方法返回是实体对象的cglib代理类
(2)如果对象不存在,直接返回null( 查询用户id为3,获取用户信息和部门信息)
六、几种情况:
情况1:当调用Session上的load()方法加载一个实体时,会采用延迟加载
情况2:当Session加载某个实体时,会对这个实体中的集合属性值采用延迟加载
情况3:当Session加载某个实体时,会对这个实体多单端关联(一对一,多对一)的另一个实体对象采用延迟加载
七、关闭延迟加载
(1)加载实体普通属性的同时,加载实体中的集合属性。在集合标签上添加属性lazy="false"
(2)加载某个实体时,不需要对这个实体单端关联的另一个实体对象延迟加载,在配置文件的配置元素(<one-to-one>,<many-to-one>添加属性lazy="false")
总结:
hibernate3以后默认启用了延迟加载:lazy="true"。hibernate对于load方法,认为该数据在数据库中一定存在,可以放心的使用代理来延迟加载,如果在使用过程中发现问题,就抛异常;对于get方法,hibernate一定要获取真实的数据,否则返回null。get是比load安全的,但是安全性是要牺牲性能的。