hibernate懒加载问题

本文介绍了Hibernate框架中的懒加载机制,包括其实现原理及其在不同场景下的应用。文章详细阐述了如何利用Session的load()方法实现懒加载,并探讨了一对一、多对一及一对多关联中的懒加载配置。

Hibernate懒加载解析

在Hibernate框架中,当我们要访问的数据量过大时,明显用缓存不太合适, 因为内存容量有限 ,为了减少并发量,减少系统资源的消耗,这时Hibernate用懒加载机制来弥补这种缺陷,但是这只是弥补而不是用了懒加载总体性能就提高了。

我们所说的懒加载也被称为延迟加载,它在查询的时候不会立刻访问数据库,而是返回代理对象,当真正去使用对象的时候才会访问数据库。

   实现懒加载的前提: 

1 实体类不能是final的

2 能实现懒加载的对象都是被CGLIB(反射调用)改写的代理对象,所以不能是final修饰的
3 须要asm,cglib两个jar包
4 相应的lazy属性为true
5 相应的fetch属性为select 
下面几种可以实现懒加载功能:


说明:

1. HibernateUtils这个类为自己写的一个工具类,用于获取SessionFactory,Session等,此处没有写出;

2. 在测试之前,我们先在hibernate.cfg.xml配置文件中加入:

<property name="show_sql">true</property>
这样我们就能在后台查看hibernate用到的底层的select、insert等语句.

一、通过Session.load()实现懒加载

load(Object, Serializable):根据id查询 。查询返回的是代理对象,不会立刻访问数据库,是懒加载的。当真正去使用对象的时候才会访问数据库。

案例:

1.当我们使用session.get()方法时,代码如下:

	public static void main(String[] args) {
		Session session = HiberanteUtils.getCurrentSession();
		Transaction tx = session.beginTransaction();
		session.get(Scores.class, 2);
	}
这样我们得到的结果是:

Hibernate: select scores0_.id as id1_0_, scores0_.cid as cid1_0_, scores0_.sid as sid1_0_, scores0_.score as score1_0_ from hqltest.scores scores0_ where scores0_.id=?
可以从这里看到,我们单纯的用get方法区Scores表中的id为2的那条记录的时候,hibernate已经用select语句查出来了,所以我们可以直接调用查询到的该Scores对象的属性。

Scores score = (Scores)session.get(Scores.class,2);
System.out.println(score.getScore());

2.当我们用load方法实现懒加载的时候,代码如下:

	public static void main(String[] args) {
		Session session = HiberanteUtils.getCurrentSession();
		Transaction tx = session.beginTransaction();
		session.load(Scores.class, 2);
	}
这样我们后台显示的为空,即说明此事用load方法查询数据库时,hibernate不会真正马上用select语句查询。

但是当我们真正用到该load方法查询到的对象的时候:

Scores scores = (Scores)session.load(Scores.class,2);
System.out.println(scores.getScore()+"分");
此时,hibernate才会在后台取出那条之前用load方法查询到的代理对象的对应的记录:

Hibernate: select scores0_.id as id1_0_, scores0_.cid as cid1_0_, scores0_.sid as sid1_0_, scores0_.score as score1_0_ from hqltest.scores scores0_ where scores0_.id=?

使用load()可以提高效率,因为刚开始的时候并没有查询数据库。但是用的不多。

使用load()时如果在session关闭之后再查询此对象,会报异常:could not initialize proxy - no Session。

处理办法

One:在session关闭之前先用Hibernate.initialize(Object);方法来显示初始化一下查询出来的对象:

public static void main(String[] args) {
		Scores scores = getScores();
		System.out.println("这门课为:"+scores.getCourses().getCname()+"\t成绩为"+scores.getScore());
	}
	private static Scores getScores() {
		Session session = null;
		Transaction tx = null;
		Scores scores;
		try {
			session = HiberanteUtils.getCurrentSession();
			tx = session.beginTransaction();
			scores = (Scores) session.load(Scores.class, 3);
			Hibernate.initialize(scores.getCourses());
			tx.commit();
		} catch (Exception e) {
			if(tx!=null)tx.rollback();
			throw new RuntimeException(e.getMessage());
		}finally{
			if(session!=null && session.isOpen()){
				session.close();
				session = null;
			}
		}
		return scores;
	}

Two:在Scores.hbm.xml对象映射配置文件中将懒加载关闭,设置lazy属性为false:

<class name="com.dai.hql.model.Scores" lazy="false" table="scores" catalog="hqltest">

Three:通过openSessionInView(过滤器)

二、  one-to-one(元素)实现了懒加载。

在一对一的时候,查询主对象时默认不是懒加载。即:查询主对象的时候也会把从对象查询出来。

需要把主对象配制成lazy="true" constrained="true"  fetch="select"。此时查询主对象的时候就不会查询从对象,从而实现了懒加载。

一对一的时候,查询从对象的是默认是懒加载。即:查询从对象的时候不会把主对象查询出来。而是查询出来的是主对象的代理对象。

3、   many-to-one(元素)实现了懒加载。

多对一的时候,查询主对象时默认是懒加载。即:查询主对象的时候不会把从对象查询出来。

多对一的时候,查询从对象时默认是懒加载。即:查询从对象的时候不会把主对象查询出来。

hibernate3.0中lazy有三个值,true,false,proxy,默认的是lazy="proxy".具体设置成什么要看你的需求,并不是说哪个设置就是最好的。在<many-to-one>与<one-to-one>标签上:当为true时,会有懒加载特性,当为false时会产生N+1问题,比如一个学生对应一个班级,用一条SQL查出10个学生,当访问学生的班级属性时Hibernate会再产生10条SQL分别查出每个学生对应的班级.

lazy= 什么时候捉取

fetch= 捉取方式:select=关联查询;join=连接表的方式查询(效率高)

fetch=join时,lazy的设置将没有意义.

4、   one-to-many(元素)懒加载:默认会懒加载,这是必须的,是重常用的。

一对多的时候,查询主对象时默认是懒加载。即:查询主对象的时候不会把从对象查询出来。

一对多的时候,查询从对象时默认是懒加载。即:查询从对象的时候不会把主对象查询出来。

需要配置主对象中的set集合lazy="false" 这样就配置成是不懒加载了。或者配置抓取方式fetch="join"也可以变成不懒加载。 

实现懒加载的方案:

方法一:(没有使用懒加载)  

用 Hibernate.initialize(de.getEmps()) 提前加载一下. 
方法二:

把与Session脱离的对象重新绑定

lock()方法是用来让应用程序把一个未修改的对象重新关联到新session的方法。

//直接重新关联

session.lock(fritz,LockMode.NONE);

//进行版本检查后关联

session.lock(izi,LockMode.READ);

//使用SELECT... FOR UPDATE进行版本检查后关联

session.lock(pk,LockMode.UPGRADE);

方法三:

OpenSessionInView

参见 http://www.javaeye.com/topic/32001          

fetch 和 lazy 配置用于数据的查询 

lazy 参数值常见有 false 和 true,Hibernate3映射文件中默认lazy = true ;

fetch 指定了关联对象抓取的方式,参数值常见是select和join,默认是select,select方式先查询主对象,再根据关联外键,每一个对象发一个select查询,获取关联的对象,形成了n+1次查询;而join方式,是leftouter join查询,主对象和关联对象用一句外键关联的sql同时查询出来,不会形成多次查询。 

在映射文件中,不同的组合会使用不同的查询: 

1、lazy="true" fetch = "select" ,使用延迟策略,开始只查询出主对象,关联对象不会查询,只有当用到的时候才会发出sql语句去查询 ;

2、lazy="false" fetch = "select" ,没有用延迟策略,同时查询出主对象和关联对象,产生1+n条sql. 

3、lazy="true"或lazy="false"fetch = "join",延迟都不会作用,因为采用的是外连接查询,同时把主对象和关联对象都查询出来了. 

另 外,在hql查询中,配置文件中设置的join方式是不起作用的,而在其他查询方式如get、criteria等是有效的,使用 select方式;除非在hql中指定join fetch某个关联对象。fetch策略用于get/load一个对象时,如何获取非lazy的对象/集合。 这些参数在Query中无效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值