在hibernate中我们知道如果要从数据库中得到一个对象,通常有两种方式,一种是通过session.get()方法,另一种就是通过session.load()方法,然后其实这两种方法在获得一个实体对象时是有区别的,在查询性能上两者是不同的。
一.load加载方式
当使用load方法来得到一个对象时,此时hibernate会使用延迟加载的机制来加载这个对象,即:当我们使用session.load()方法来加载一个对象时,此时并不会发出sql语句,当前得到的这个对象其实是一个代理对象,这个代理对象只保存了实体对象的id值,只有当我们要使用这个对象,得到其它属性时,这个时候才会发出sql语句,从数据库中去查询我们的对象。
session = HibernateUtil.getSession();
Type type = (Type) session.load(Type.class, 9);
通过load的方式加载对象时,会使用延迟加载机制,此时并不会发出sql语句,只有当我们需要使用的时候才会从数据库中去查询
请看下面的一段代码:
@Test
public void testLoad() {
Session session = null;
try {
session = HibernateUtil.getSession();
Type type = (Type) session.load(Type.class, 9);
System.out.println("load加载 时 类型 "+ type.getClass());
Type t = (Type) session.get(Type.class, 11);
System.out.println("get加载 时 类型 "+t.getClass());
System.out.println("load 加载 sql 语句的发送时间 ------");
System.out.println(type.getName());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (session != null) {
session.close();
}
}
}
请看执行结果:
load加载 时 类型 class com.lgh.hibernate.entity.Type_$$_jvstd0_0
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
get加载 时 类型 class com.lgh.hibernate.entity.Type
load 加载 sql 语句的发送时间 ------
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
水果
通过结果我们发现 当我们使用session.load()方法来加载一个对象时,此时并不会发出sql语句,当前得到的这个对象其实是一个代理对象,这个代理对象只保存了实体对象的id值,实际上我们得到的是hibernate 帮我们动态生成的Type 子类 ,如果我们要使用该对象时获取除ID的其他属性时,此时我们看到控制台会发出了sql查询语句,会将该对象从数据库中查询出来
二、get加载方式
相对于load的延迟加载方式,get就直接的多,当我们使用session.get()方法来得到一个对象时,不管我们使不使用这个对象,此时都会发出sql语句去从数据库中查询出来
@Test
public void testGet() {
Session session = null;
try {
session = HibernateUtil.getSession();
Type t = (Type) session.get(Type.class, 11);
System.out.println("get加载 时 类型 "+t.getClass());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (session != null) {
session.close();
}
}
}
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
get加载 时 类型 class com.lgh.hibernate.entity.Type
get与load 的 另一些区别
get 和load 在加载方式上的不同 也使它们查询ID不存在的对象时所引发异常也不相同
如果使用get方式来加载对象,当我们试图得到一个id不存在的对象时,此时会报NullPointException的异常
Type t = (Type) session.get(Type.class, 3434);
System.out.println("get加载 时 类型 "+t.getClass());
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
java.lang.NullPointerException
at com.lgh.hibernate.test02.HibernateCache.testGet(HibernateCache.java:121)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
....
这是因为通过get方式我们会去数据库中查询出该对象,但是这个id值不存在,所以此时Type对象是null,所以就会报NullPointException的异常了。
如果使用load方式来加载对象,当我们试图得到一个id不存在的对象时,此时会报ObjectNotFoundException异常:
session = HibernateUtil.getSession();
Type t = (Type) session.load(Type.class, 3434);
System.out.println("load加载 时 类型 "+t.getClass());
System.out.println(t.getName());
load加载 时 类型 class com.lgh.hibernate.entity.Type_$$_jvstd0_0
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.lgh.hibernate.entity.Type#3434]
为什么使用load的方式和get的方式来得到一个不存在的对象报的异常不同呢??其原因还是因为load的延迟加载机制,使用load时,此时的Type对象是一个代理对象,仅仅保存了当前的这个id值,当我们试图得到该对象的name属性时,这个属性其实是不存在的,所以就会报出ObjectNotFoundException这个异常了。
我们在编写javaweb 项目 时通常 都采用分层写法 ,把像操作数据库的的一些方法封装到一个类中,统一管理 ,一般我们都把它们命名为Dao 层 ,现在我们模拟一个TypeDao
类的getType()方法
package com.lgh.hibernate.test02;
import org.hibernate.Session;
import com.lgh.hibernate.entity.Type;
import com.lgh.hibernate.util.HibernateUtil;
public class TypeDao {
public static void main(String[] args) {
TypeDao hc = new TypeDao();
Type t = hc.getType();
System.out.println("----"+t.getName());
}
public Type getType(){
Session session = null;
try {
session = HibernateUtil.getSession();
Type t = (Type) session.load(Type.class, 11);
return t;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (session != null) {
session.close();
}
}
return null;
}
}
模拟了一个TypeDao这样的对象,然后我们在测试用例里面来通过load加载一个对象,此时我们发现控制台会报org.hibernate.LazyInitializationException: could not initialize proxy - no Session
Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:286)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
at com.lgh.hibernate.entity.Type_$$_jvst5e0_0.getName(Type_$$_jvst5e0_0.java)
at com.lgh.hibernate.test02.TypeDao.main(TypeDao.java:13)
这个异常是什么原因呢??还是因为load的延迟加载机制,当我们通过load()方法来加载一个对象时,此时并没有发出sql语句去从数据库中查询出该对象,当前这个对象仅仅是一个只有id的代理对象,我们还并没有使用该对象,但是此时我们的session已经关闭了,所以当我们在测试用例中使用该对象时就会报LazyInitializationException这个异常了。
所以以后我们只要看到控制台报LazyInitializationException这种异常,就知道是使用了load的方式延迟加载一个对象了,解决这个的方法有三种,一种是将load改成get的方式来得到该对象,另一种是在表示层来开启我们的session和关闭session,还有一种就是。
在dao层load方法后面加上:
Hibernate.initialize(t);
public Type getType() {
Session session = null;
try {
session = HibernateUtil.getSession();
Type t = (Type) session.load(Type.class, 11);
// 强制初始化 发送SQL语句向数据库拿数据
Hibernate.initialize(t);
return t;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (session != null) {
session.close();
}
}
return null;
}
Load延迟加载的管理
通过上面我们知道使用load方法会产生延迟加载现象那怎么配置让load方法跟get方法一样呢
这有多种方式:
在配置实体类的映射文件添加lazy属性
lazy 属性默认为true 当我们不配置或为true时 ,通过load方法就会有延迟加载,当lazy = “false”时 load方法就会与get方法没什么区别了
<hibernate-mapping>
<class name="com.lgh.hibernate.entity.Type" table="T_TYPE" lazy="false">
.....
</class>
<!-- 我们编写的HQL语句 -->
<query name="getAll">
from Type where id > ?
</query>
</hibernate-mapping>
Type t = (Type) session.load(Type.class, 11);
System.out.println("load加载 时 类型 "+t.getClass());
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
load加载 时 类型 class com.lgh.hibernate.entity.Type
我们发现 我们没有访问Type的其他属性 但是当调用load方法时 发出了查询语句 并且此时返回值的类型不再是代理子类 而是type类本身
看到了上面的代码,我们注意到 load 查找时返回返回值的类型是代理子类,其是要继承Type类的才可以产生代理子类 那么当Type类为final 时 ,不能被继承load方法是否还会延迟加载呢?
第二种方法 把实体类 final
Type t = (Type) session.load(Type.class, 11);
System.out.println("load加载 时 类型 "+t.getClass());
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
load加载 时 类型 class com.lgh.hibernate.entity.Type
我们看到把类修改成不能被继承 此时延迟加载 也不能发挥作用了
关联对象的延迟加载
一对多关联
Type 与Product 时 一对多关系
当在一对多关联映射模式下
fetch="select"
<!-- class="com.lgh.hibernate.entity.Type" fetch="join" -->
<!-- fetch 控制如何查关联对象,
join 采用外连接去拿关联对象 没有产生延迟加载行为
select 另外发送查询语句查询
lazy : 三种 值:
- false
- proxy
- no-proxy
-->
<many-to-one name="type" cascade="save-update" fetch="select" >
<column name="tid" />
</many-to-one>
当没有配置lazy 时
我们同过 get方法:
session = HibernateUtil.getSession();
Product p = (Product) session.get(Product.class, 26);
System.out.println("-------------");
System.out.println(p.getType().getName());
结果:
Hibernate: select product0_.ID as ID1_3_0_, product0_.NAME as NAME2_3_0_, product0_.PATH as PATH3_3_0_, product0_.DESCRI as DESCRI4_3_0_, product0_.PRICE as PRICE5_3_0_, product0_.tid as tid6_3_0_ from T_PRODUCT product0_ where product0_.ID=?
-------------
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
水果
我们发现 在 需要用到type 时 才会向数据库查询 在查询关联对象时发生延迟加载
当使用 load方法时:
session = HibernateUtil.getSession();
Product p = (Product) session.load(Product.class, 26);
System.out.println("---------------");
System.out.println(p.getName());
System.out.println("-------------");
System.out.println(p.getType().getName());;
---------------
Hibernate: select product0_.ID as ID1_3_0_, product0_.NAME as NAME2_3_0_, product0_.PATH as PATH3_3_0_, product0_.DESCRI as DESCRI4_3_0_, product0_.PRICE as PRICE5_3_0_, product0_.tid as tid6_3_0_ from T_PRODUCT product0_ where product0_.ID=?
西瓜
-------------
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
水果
我们发现 使用product对象其他属性时向数据库发送查询语句
在 需要用到 type 时 才会向数据库查询 在查询关联对象时发生延迟加载
当我们配置
lazy="false" fetch="select"
<!-- class="com.lgh.hibernate.entity.Type" fetch="join" -->
<!-- fetch 控制如何查关联对象,
join 采用外连接去拿关联对象 没有产生延迟加载行为
select 另外发送查询语句查询
lazy : 三种 值:
- false
- proxy
- no-proxy
-->
<many-to-one name="type" cascade="save-update" lazy="false" fetch="select" >
<column name="tid" />
</many-to-one>
load方法 :
session = HibernateUtil.getSession();
Product p = (Product) session.load(Product.class, 26);
System.out.println("---------------");
System.out.println(p.getName());
System.out.println("-------------");
System.out.println(p.getType().getName());
---------------
Hibernate: select product0_.ID as ID1_3_0_, product0_.NAME as NAME2_3_0_, product0_.PATH as PATH3_3_0_, product0_.DESCRI as DESCRI4_3_0_, product0_.PRICE as PRICE5_3_0_, product0_.tid as tid6_3_0_ from T_PRODUCT product0_ where product0_.ID=?
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
西瓜
-------------
水果
我们 发现 当使用product对象其他属性时向数据库发送查询语句 同时页发送关联对象的查询语句
get方法:
session = HibernateUtil.getSession();
Product p = (Product) session.get(Product.class, 26);
System.out.println("---------------");
System.out.println(p.getName());
System.out.println("-------------");
System.out.println(p.getType().getName());
Hibernate: select product0_.ID as ID1_3_0_, product0_.NAME as NAME2_3_0_, product0_.PATH as PATH3_3_0_, product0_.DESCRI as DESCRI4_3_0_, product0_.PRICE as PRICE5_3_0_, product0_.tid as tid6_3_0_ from T_PRODUCT product0_ where product0_.ID=?
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
---------------
西瓜
-------------
水果
在向数据库查询时同时也发送了查询关联对象的SQL语句
多对多关联的延迟加载
teacher 和 student 属于多对多关联
当 lazy="true" 时 会延迟加载,当为false时
<set name="teachers" table="TEACHER_STUDENT" inverse="false" lazy="false" cascade="save-update" >
<key column="SID" />
<!--
lazy的值:
- true
- false
- extra
-->
<many-to-many column="TID" class="com.lgh.hibernate.entity.Teacher"></many-to-many>
</set>
session = HibernateUtil.getSession();
Student s = (Student) session.get(Student.class, 1);
System.out.println("---------------");
System.out.println(s.getName());
System.out.println("-------------");
Hibernate: select student0_.ID as ID1_4_0_, student0_.NAME as NAME2_4_0_ from T_STUDENT student0_ where student0_.ID=?
Hibernate: select teachers0_.SID as SID2_4_0_, teachers0_.TID as TID1_2_0_, teacher1_.ID as ID1_5_1_, teacher1_.NAME as NAME2_5_1_ from TEACHER_STUDENT teachers0_ inner join T_TEACHER teacher1_ on teachers0_.TID=teacher1_.ID where teachers0_.SID=?
Hibernate: select students0_.tid as tid1_5_0_, students0_.sid as sid2_2_0_, student1_.ID as ID1_4_1_, student1_.NAME as NAME2_4_1_ from TEACHER_STUDENT students0_ inner join T_STUDENT student1_ on students0_.sid=student1_.ID where students0_.tid=?
Hibernate: select students0_.tid as tid1_5_0_, students0_.sid as sid2_2_0_, student1_.ID as ID1_4_1_, student1_.NAME as NAME2_4_1_ from TEACHER_STUDENT students0_ inner join T_STUDENT student1_ on students0_.sid=student1_.ID where students0_.tid=?
Hibernate: select teachers0_.SID as SID2_4_0_, teachers0_.TID as TID1_2_0_, teacher1_.ID as ID1_5_1_, teacher1_.NAME as NAME2_5_1_ from TEACHER_STUDENT teachers0_ inner join T_TEACHER teacher1_ on teachers0_.TID=teacher1_.ID where teachers0_.SID=?
---------------
张华
-------------
延迟加载已经没有了
一对一的延迟加载
一对一与一对多相同 这里就不再讲述了
<!--
lazy : 三种 值:
- false
- proxy
- no-proxy
-->
<one-to-one name="person" class="com.lgh.hibernate.entity.Person" cascade="save-update" ></one-to-one>
hibernate的延迟加载就讲到这里 ,有什么错误的地方还请大家指出来