一、简介
- 延迟加载就是我们真正使用某个对象的时候,这个对象才会被创建出来。而在hibernate中的意思就是只有我们真正需要使用某个对象的时候,才会去查询。采用第三方的一个库cglab,生成代理类。和jdk的动态代理的区别是cglab能对类进行代理(继承原先的类生成一个子类,子类作为代理类),而jdk的动态代理只能对实现了接口的类进行代理。
-
hibernate的lazy策略可以使用在:
-
<class>
标签上,可以取值:true、false,默认是true,打开。 -
<property>
标签上,可以取值true、false,默认是true打开,使用较少。需要类增强工具(不讲)。 -
<set><list>
标签上,可以取值true、false、extra -
<one_to_one><many_to_one>
标签中,单端关联上,可以取值:false、proxy、noproxy
-
代理的概念:只有真正使用该对象时才会创建,对于hibernate来说就是只有真正使用的时候才会发出sql。注意:lazy和Session的生命周期一致。
二、用在class标签上(工程hibernate_lazy4class
)
实体类(Group.java
)
package cn.itcast.hibernate;
public class Group {
private int id ;
private String name ;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Group.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.hibernate.Group" table="_group">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>
先在数据库中存入一些数据:
InitData.java
package cn.itcast.hibernate;
import org.hibernate.Session;
public class InitData {
public static void main(String[] args) {
Session session = null ;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Group group = new Group();
group.setName("高二班");
session.save(group);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
}
测试:
ClassLazyTest.java
package cn.itcast.hibernate;
import org.hibernate.Session;
import org.junit.Test;
/*
*在测试的时候有个条件是:设置<class>标签上的lazy=true,也就是默认配置
* */
public class ClassLazyTest {
@Test
public void testLoad1(){
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//不会发sql,因为没有真正用到
Group gorup = (Group) session.load(Group.class, 1);
//这里是不会发sql的,因为主键在上面已经给出了
System.out.println("group.id = " + gorup.getId());
//这里是要发sql的,因为需要用到
System.out.println("group.name = " + gorup.getName());
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
@Test
public void testLoad2(){
Session session = null;
Group group = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
group = (Group) session.load(Group.class, 1);
System.out.println("group.id = " + group.getId());
System.out.println("group.name = " + group.getName());
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
//不能正确执行,抛出异常,因为session已经关闭,hibernate支持lazy策略只有在session打开状态下有效
System.out.println("group.name = " + group.getName());
}
}
三、用在集合标签上(工程hibernate_lazy4collection
)
这里我们使用一对多双向关联的例子。
实体类:
Student.java
private int id ;
private String name ;
private Classes classes;
Classes.java
private int id ;
private String name ;
private Set students;
配置:
Student.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.hibernate.Student" table="_student">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<many-to-one name="classes" column="classesid"/>
</class>
</hibernate-mapping>
Classes.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.hibernate">
<class name="Classes" table="_classes">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="students" inverse="true" cascade="all" >
<key column="classesid"/>
<one-to-many class="Student"/>
</set>
</class>
</hibernate-mapping>
首先我们需要在数据库中存入几条数据。
测试:
- 1.保持lazy默认
CollectionLazyTest1.java
package cn.itcast.hibernate;
import java.util.Iterator;
import java.util.Set;
import org.hibernate.Session;
import org.junit.Test;
/*保持lazy默认*/
public class CollectionLazyTest1 {
@Test
public void testLoad1(){
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//不会发出sql
Classes classes = (Classes) session.load(Classes.class, 1);
//会发出sql
System.out.println("classes.name = " + classes.getName());
//不会发出sql
Set students = classes.getStudents();
//会发出sql
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println("student.name=" + student.getName());
}
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
@Test
public void testLoad2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//不会发出sql
Classes classes = (Classes)session.load(Classes.class, 1);
//会发出sql
System.out.println("classes.name=" + classes.getName());
//不会发出sql
Set students = classes.getStudents();
//会发出sql,发出查询全部数据的sql
System.out.println("student.count=" + students.size());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
说明:这里我们可以看到在默认情况下,不管是集合还是普通属性都是支持延迟加载的。
- 2.设置<class>标签上的lazy=false
CellectionlazyTest2.java
package cn.itcast.hibernate;
import java.util.Iterator;
import java.util.Set;
import junit.framework.TestCase;
import org.hibernate.Session;
/**
* 设置<class>标签上的lazy=false
* @author Administrator
*
*/
public class CellectionlazyTest2 extends TestCase {
public void testLoad1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//会发出sql
Classes classes = (Classes)session.load(Classes.class, 1);
//不会发出sql
System.out.println("classes.name=" + classes.getName());
//不会发出sql
Set students = classes.getStudents();
//会发出sql
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println("student.name=" + student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
说明:看以看到,当我们将class标签上的lazy设置为false的时候,对于普通属性就不支持延迟加载了,但是对于集合来说,还是支持延迟加载的,这说明,class上的lazy对于集合是没有影响的。
- 3.设置集合上的lazy=false,其它默认
CellectionlazyTest3.java
package cn.itcast.hibernate;
import java.util.Iterator;
import java.util.Set;
import junit.framework.TestCase;
import org.hibernate.Session;
/**
* 设置集合上的lazy=false,其它默认
*
*/
public class CellectionlazyTest3 extends TestCase {
public void testLoad1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//不会发出sql
Classes classes = (Classes)session.load(Classes.class, 1);
//会发出sql,会发出两条sql分别加载Classes和Student
System.out.println("classes.name=" + classes.getName());
//不会发出sql
Set students = classes.getStudents();
//不会发出sql
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println("student.name=" + student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
public void testLoad2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//不会发出sql
Classes classes = (Classes)session.load(Classes.class, 1);
//会发出sql,会发出两条sql分别加载Classes和Count
System.out.println("classes.name=" + classes.getName());
//不会发出sql
Set students = classes.getStudents();
//不会发出sql
System.out.println("student.count=" + students.size());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
说明:由于将集合的lazy设置为false,则集合不支持延迟加载,那么在使用到Classes的时候就会将查询集合的sql语句也发出去。而在之后的操作中不再发送sql语句。但是在查询集合中元素个数的时候不是发送的count语句,而是直接将集合整体查出来,再统计。
- 4.设置集合上的lazy=extra,其它默认
CellectionlazyTest4.java
package cn.itcast.hibernate;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import junit.framework.TestCase;
import org.hibernate.Session;
/**
* 设置集合上的lazy=extra,其它默认
* @author Administrator
*/
public class CellectionlazyTest4 extends TestCase {
public void testLoad1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//不会发出sql
Classes classes = (Classes)session.load(Classes.class, 1);
//会发出sql
System.out.println("classes.name=" + classes.getName());
//不会发出sql
Set students = classes.getStudents();
//会发出sql
for (Iterator iter=students.iterator(); iter.hasNext();) {
Student student = (Student)iter.next();
System.out.println("student.name=" + student.getName());
}
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
public void testLoad2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//不会发出sql
Classes classes = (Classes)session.load(Classes.class, 1);
//会发出sql
System.out.println("classes.name=" + classes.getName());
//不会发出sql
Set students = classes.getStudents();
//会发出sql,发出一条比较智能的sql
System.out.println("student.count=" + students.size());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
说明:对于直接查询出集合对象,和lazy为true的情况是一样的,是在使用的时候才会发出,但是对于查询集合中元素的个数则和上个例子不一样了,这里是发送的count语句,效率会比较高,所以在实际开发中一般使用此属性。
四、用在单端关联上(工程hibernate_lazy4single_end
)
单端关联有<one-to-one>和<many-to-one>,两者比较类似,这里我们使用后者作为例子。在单端关联上的lazy策略可以取值为:false、proxy、noproxy。
相关实体类:
Group.java
private int id;
private String name;
User.java
private int id;
private String name;
private Group group;
相关配置:
Group.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.hibernate">
<class name="Group" table="_group">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>
User.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.hibernate">
<class name="User" table="_user" lazy="false">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<many-to-one name="group" column="groupid"/>
</class>
</hibernate-mapping>
先想数据库中存入一些数据:
InitData.java
package cn.itcast.hibernate;
import org.hibernate.Session;
public class InitData {
public static void main(String[] args) {
Session session = null ;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Group group = new Group();
group.setName("高一班");
session.save(group);
User user1 = new User();
user1.setName("张三");
user1.setGroup(group);
User user2 = new User();
user2.setName("李四");
user2.setGroup(group);
session.save(user1);
session.save(user2);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
}
测试:
- 1.<many-to-one>的lazy保持默认,即proxy。
SingleEndTest1.java
package cn.itcast.hibernate;
import org.hibernate.Session;
import org.junit.Test;
/**
* 所有lazy属性默认
*/
public class SingleEndTest1{
@Test
public void testLoad1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//不会发出sql
User user = (User)session.load(User.class, 1);
//会发出sql
System.out.println("user.name=" + user.getName());
//不会发出sql
Group group = user.getGroup();
//会发出sql
System.out.println("group.name=" + group.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
说明:可以看到这里的情况和集合的lazy策略一样,都是在使用的时候才发出sql语句。
- 2.将<many-to-one>中的lazy设置为false,其它默认
SingleEndTest2.java
package cn.itcast.hibernate;
import org.hibernate.Session;
import org.junit.Test;
import junit.framework.TestCase;
/**
* 将<many-to-one>中的lazy设置为false,其它默认
*/
public class SingleEndTest2{
@Test
public void testLoad1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//不会发出sql
User user = (User)session.load(User.class, 1);
//会发出sql,发出两条sql分别加载User和Group
System.out.println("user.name=" + user.getName());
//不会发出sql
Group group = user.getGroup();
//不会发出sql
System.out.println("group.name=" + group.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
说明:此时不支持延迟加载了,即在使用User的时候就会将Group查询出来。
- 3.<class>标签上的lazy=false,其它默认
SingleEndTest3.java
package cn.itcast.hibernate;
import org.hibernate.Session;
import org.junit.Test;
/**
* <class>标签上的lazy=false,其它默认
*/
public class SingleEndTest3 {
@Test
public void testLoad1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
//会发出sql
User user = (User)session.load(User.class, 1);
//不会发出sql
System.out.println("user.name=" + user.getName());
//不会发出sql
Group group = user.getGroup();
//会发出sql
System.out.println("group.name=" + group.getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
说明:将普通属性的lazy设置为false对单端关联没有影响,单端关联的lazy策略此时还是proxy。对于noproxy用的不多,这里不再说明。