一、首先了解几个问题:
1、什么是懒加载?
所谓懒加载也就是延迟加载。
2、什么时候使用懒加载?
当我们去查询某项数据,此项数据有与其关联的数据,而对于所关联的数据,我们并不确定是否需要,此时就可以使用懒加载,来减少不必要的内存消耗,让数据在真正使用的时候才去加载。
比如:Department和Employee。部门与员工是1对多的关系,如果不进行懒加载,那么只要加载一个部门,就会根据此部门来加载所有的员工,但实际上,我们只是需要用到部门的信息,而将员工信息也加载进内存显然就造成了浪费。如果我们设置成懒加载,那么只有你在具体的使用带员工的信息的时候才会去加载员工的信息。
3、Hibernate中懒加载的实现:
hibernate的懒加载是利用动态代理来实现的;我们知道动态代理有JDK的动态代理(JDK动态代理所代理的对象必须要实现一个接口)和CGLIB的动态代理,Hibernate的懒加载采用的是CGLIB的动态代理,CGLIB的动态代理可以生成目标类的子类,这也就是为什么创建对象关系映射的时候要求实体类不能够为final类型的原因了。
4、Hibernate懒加载(lazy)在何处使用?
- <class>标签上,可以取值有true、false(默认为true)
- <property>标签上,可以取值有true、false
- <set>、<list>集合上,可以取值true、false、extra(默认为true)
- <one-to-one>、<many-to-one>单端关联上,可以取值:false/proxy/no-proxy
二、get()和load()的懒加载:
实例一:
get()方法不支持懒加载
@Test
public void get1() {
Session session = null;
Transaction tran = null;
try {
session = SessionUtil.getSession();
tran = session.beginTransaction();
//执行此语句,立即加载,发出sql语句
TBook book = (TBook) session.get(TBook.class, 1);
System.out.println("书名:" + book.getBName());
tran.commit();
} catch (Exception e) {
tran.rollback();
e.printStackTrace();
} finally {
SessionUtil.close(session);
}
}
示例二:
load()方法支持懒加载(默认<class lazy="true">)
@Test
public void load1() {
Session session = null;
Transaction tran = null;
try {
session = SessionUtil.getSession();
tran = session.beginTransaction();
//执行此语句不会发出sql,只会返回一个代理对象
TBook book = (TBook) session.load(TBook.class, 1);
//不会发出sql语句,使用使用输入的1
System.out.println("book.getId():"+book.getId());
//真正使用数据时,发出sql语句
System.out.println("书名:"+book.getBName());
} catch (Exception e) {
tran.rollback();
e.printStackTrace();
} finally {
SessionUtil.close(session);
}
}
load()方法支持懒加载(默认<class lazy="true">),提前关闭session
@Test
public void load2(){
Session session = null;
try{
session = SessionUtil.getSession();
TBook book = (TBook)session.load(TBook.class, 1);
//提前关闭session
SessionUtil.close(session);
//不能正确输出,抛出了LazyInitalizationException异常
//could not initialize proxy - no Session
System.out.println("书名:" + book.getBName());
}
catch(Exception e){
e.printStackTrace();
}
finally{
SessionUtil.close(session);
}
}
三、关联映射中集合set和list中的lazy属性
示例一:
集合上的lazy=true(默认),其它默认。
@Test
public void testLoad1() {
Session session = null;
Transaction tran = null;
try {
session = SessionUtil.getSession();
tran = session.beginTransaction();
// 不发出sql,因为load对于实体默认懒加载
Category c = (Category) session.load(Category.class, 1);
// 发出sql,因为使用了该对象
System.out.println("分类名称:" + c.getCategory());
// 不发出sql,因为集合是懒加载,返回一个代理类。
Set<TBook> tBooks = c.getTBooks();
// 发出sql,因为使用了对象
Iterator<TBook> it = tBooks.iterator();
while(it.hasNext()){
System.out.println(it.next().getBName());
}
tran.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
SessionUtil.close(session);
}
}
集合上的lazy=true(默认),其他默认。
@Test
public void testLoad1() {
Session session = null;
Transaction tran = null;
try {
session = SessionUtil.getSession();
tran = session.beginTransaction();
// 不发出sql,因为load对于实体默认懒加载
Category c = (Category) session.load(Category.class, 1);
// 发出sql,因为使用了该对象
System.out.println("分类名称:" + c.getCategory());
// 不发出sql,因为集合是懒加载,返回一个代理类。
Set<TBook> tBooks = c.getTBooks();
// 发出sql,发出查询全部字段的sql,效率不高
System.out.println("该类别图书数量"+tBooks.size());
tran.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
SessionUtil.close(session);
}
}
实例三:
集合上的lazy=false,其他默认。
@Test
public void testLoad1() {
Session session = null;
Transaction tran = null;
try {
session = SessionUtil.getSession();
tran = session.beginTransaction();
// 不发出sql,因为load对于实体默认懒加载
Category c = (Category) session.load(Category.class, 1);
// 发出两条sql,分别加载Category和TBook
//并且把集合中的数据也加载上来(虽然并没有使用集合中的对象),因为设置了集合的lazy=false
System.out.println("分类名称:" + c.getCategory());
// 不发出sql,已经在前面加载了数据
Set<TBook> tBooks = c.getTBooks();
//不发出sql,前面已经加载了数据
Iterator<TBook> it = tBooks.iterator();
while(it.hasNext()){
System.out.println(it.next().getBName());
}
System.out.println("该类别图书数量"+tBooks.size());
tran.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
SessionUtil.close(session);
}
}
实例四:
集合上的lazy=false,其它默认。
@Test
public void testLoad1() {
Session session = null;
Transaction tran = null;
try {
session = SessionUtil.getSession();
tran = session.beginTransaction();
// 不发出sql,因为load对于实体默认懒加载
Category c = (Category) session.load(Category.class, 1);
// 发出两条sql,分别加载Category和TBook
//并且把集合中的数据也加载上来(虽然并没有使用集合中的对象),因为设置了集合的lazy=false
System.out.println("分类名称:" + c.getCategory());
// 不发出sql,已经在前面加载了数据
Set<TBook> tBooks = c.getTBooks();
//不发出sql,前面已经加载了数据
System.out.println(tBooks.size());
} catch (Exception e) {
e.printStackTrace();
} finally {
SessionUtil.close(session);
}
}
示例五:
集合上的lazy=extra,其它默认
@Test
public void testLoad1() {
Session session = null;
Transaction tran = null;
try {
session = SessionUtil.getSession();
tran = session.beginTransaction();
// 不发出sql,因为load对于实体默认懒加载
Category c = (Category) session.load(Category.class, 1);
//会发出sql
System.out.println("分类名称:" + c.getCategory());
// 不发出sql,只返回代理对象
Set<TBook> tBooks = c.getTBooks();
//会发出sql
Iterator<TBook> iterator = tBooks.iterator();
} catch (Exception e) {
e.printStackTrace();
} finally {
SessionUtil.close(session);
}
}
示例六:
集合上的lazy=extra,其它默认。
@Test
public void testLoad1() {
Session session = null;
Transaction tran = null;
try {
session = SessionUtil.getSession();
tran = session.beginTransaction();
// 不发出sql,因为load对于实体默认懒加载
Category c = (Category) session.load(Category.class, 1);
//会发出sql
System.out.println("分类名称:" + c.getCategory());
// 不发出sql,只返回代理对象
Set<TBook> tBooks = c.getTBooks();
//会发出sql,发出一条比较智能的sql
//(select count(id) from admindb.t_book where category_Id =?)
System.out.println(tBooks.size());
} catch (Exception e) {
e.printStackTrace();
} finally {
SessionUtil.close(session);
}
}
四、<one-to-one>、<many-to-one>单端关联上的lazy(懒加载)属性
示例一:
所有的lazy属性默认(支持懒加载)
@Test
public void testLoad() {
Session session = null;
Transaction tran = null;
try {
session = SessionUtil.getSession();
tran = session.beginTransaction();
//不发出sql
TBook b = (TBook) session.load(TBook.class, 1);
//发出sql,只加载TBook,不会加载Category
System.out.println("图书名称:" + b.getBName());
//不发出sql,返回代理对象
Category category = b.getCategory();
//真正使用对象时,发出sql
System.out.println("分类名称:" + category.getCategory());
tran.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
SessionUtil.close(session);
}
}
将<many-to-one>中lazy属性设为false,其它默认。
@Test
public void testLoad() {
Session session = null;
Transaction tran = null;
try {
session = SessionUtil.getSession();
tran = session.beginTransaction();
//不发出sql
TBook b = (TBook) session.load(TBook.class, 1);
//发出两条sql,分别是TBook和Category
System.out.println("图书名称:" + b.getBName());
//不发出sql,上面已经加载数据
Category category = b.getCategory();
//不发出sql,上面已经加载数据
System.out.println("分类名称:" + category.getCategory());
tran.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
SessionUtil.close(session);
}
}