- 相同点
- 区别
- 懒加载的问题
执行语句 session.load(UserInfoBean.class,2)
会先在一级缓存中找 id=2 的 UserInfoBean 对象(看红色线条),找不到去二级缓存找,还找不到就不找了,直接获取一个代理对象并返回该对象。当需要获取代理对象中的属性值时,则直接去数据库查(看蓝色线条),就是所谓的懒加载。
执行语句 session.get(UserInfoBean.class,2)
,由于 load 方法已经查过 id=2 这个对象,所以这个对象(其实是一个代理对象)已经保存在缓存中了,因此 get() 方法可以在缓存区中直接获取到这个对象(看灰色线条)。
执行语句 session.get(UserInfoBean.class,3)
,由于 id=3 这个对象没有查过,get() 方法先去一级缓存中找 id=3 的对象(看紫色线条),找不到去二级缓存找,还找不到就去数据库找。
1.两个方法均可以根据指定的实体类和 id 从数据库读取记录,并返回与之对应的实体对象。
代码如下:
session.load(Employee.class,1);
session.get(Employee.class,1);
2.两个方法都支持缓存机制,即先到一级缓存区获取数据;若没有则到二级缓存区获取数据;还是没有则访问数据库获取数据。
区别1.如果未能发现符合条件的记录(即数据库不存在指定 id 的记录),get() 方法返回 null,而 load() 方法会抛出一个 ObjectNotFoundException。
// 对应的数据表中并不存在id=2的记录
Employee employee = session.get(Employee.class, 2);
System.out.println(employee);// null
当 load()
会返回的是一个实体代理对象,那么该代理对象在执行 getXxx()
时,会直接去 DB 中查找数据,如果查找不到数据,就会抛出ObjectNotFoundException 异常。
2.load() 方法支持懒加载机制(延迟加载机制),get() 不支持懒加载机制
当 类名.hbm.xml
配置文件 元素的 lazy 属性设置为 true 时(默认为 true),调用
load()
方法时 load() 先到缓存(session缓存/二级缓存)中去查,如果没有则返回一个代理类实例(不马上到 DB 中去找),这个代理类是运行时动态生成的,是实体类的子类。该代理类实例包括原目标对象(实体对象)的所有属性和方法,该代理类实例的属性除了 ID 有值外,其它属性都是默认值。查看日志并没有 Hibernate SQL 输出,说明没有执行查询操作。当代理类实例通过 getXXX()
方法获取属性值时,Hiberante 才到 DB 中查询数据。
当 类名.hbm.xml
配置文件 元素的 lazy 属性设置为 false 时,调用
load()
方法与调用 get()
的效果相同,即只会返回实体类实例,并不返回代理类实例。而调用 get()
方法时不管 lazy 为何值,都直接返回实体类实例。get()
先到缓存(session缓存/二级缓存)中去查,如果没有就到 DB 中去查(即马上发出 sql)。
疑问: 当开启懒加载机制后,load() 是先去缓存区(一级/二级缓存)中查找对象,找到则返回给对象,找不到则返回一个代理对象,还是说不会去缓存区查找对象,而是直接返回代理对象呢?或者说 load() 方法首先查询session缓存,没有就创建代理对象,实际使用数据时才查询二级缓存和数据库,是这样吗?
答: 当开启懒加载机制后,load() 是先去缓存区(一级/二级缓存)中查找对象,找到则返回给对象,找不到则返回一个代理对象。 当调用代理对象的getXxx() 方法时,不会再去缓存区获取数据,而是直接访问数据库获取数据。
代理对象: 在 Hibernate 中,所谓代理对象就是依据指定的类型和给定的 ID 值(标识符值/Identifier)动态生成的虚假对象(伪对象)。该对象中除了 ID,其它属性则初始化为默认值
注意点: get 方法如果在 session缓存中找到了指定 id 对应的对象,如果刚好该对象前面是被代理过的(如被 load 方法使用过,或者被其他关联对象延迟加载过),那么返回的还是原先的代理对象,而不是实体类对象;如果该代理对象还没有加载实体数据(就是 id 以外的其他属性数据),那么会查询二级缓存或者数据库来加载数据,但是返回的还是代理对象,只不过已经加载了实体数据。
懒加载的问题Project project = session.load(Project.class, id);
如果之前没有获取过指定id的Project 对象,那么方法load() 返回的Project对象是代理类实例,里面没有数据(除了id),只有从中获取数据填充表单时才会到数据库中取数据。
如果在 ProjectDao 中调用 session.close() 关闭了 session,那么就无法取到数据。
这就出现一个矛盾: 一方面必须关闭 Session 对象,否则浪费数据库连接资源; 另一方面,当使用延迟加载机制时,又不能在DAO中直接调用 session.close() 来关闭 Session 对象。
那么如何在项目中既可以使用延迟加载机制,又能关闭 Session 对象呢?
使用 OpenSessioninView 技术,即在页面获取数据的时候 Session 还是开启的,当页面获取完数据后再关闭 Session 对象。
如何让页面获取完数据后才关闭 Session 对象呢? 1.将关闭 session 的操作写在拦截器中,等页面获取完数据后才关闭 session。 2.关闭 session 时,要找到对应的 session(要一直持有该 session)
如何一直持有同一个 Session 对象呢? 通过 ThreadLocal 来实现 Session 的线程单例。