Hibernate查询加载策略

引言:当Hibernate查询部门对象时,立即查询并加载与之的员工对象,这查询策略是立即加载策略。立即加载存在两大不足:会执行不必要的查询语句,影响性能。可能会加载大量不需要的对象,增加系统开销,浪费内存空间。Hibernate提供了延迟加载策略,延迟加载策略能避免加载应用程序不需要访问的关联对象。

延迟加载级别

  • 类级别:<class>元素中lazy属性可选"true"延迟加载和"false"立即加载,默认是"true"延迟加载。
  • 一对多和多对多级联级别:<set>元素中lazy属性可选true延迟加载,extra增强延迟加载,false立即加载。默认值true延迟加载
  • 多对一关联级别:<many-to-one>元素中lazy属性可选proxy延迟加载,no-proxy无代理延迟加载和false立即加载,默认proxy

类级别加载策略

  • 立即加载
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.pojo.Dept" table="dept" catalog="project" lazy="false">
        <id name="deptNo" column="deptNo" type="java.lang.Byte">
            <generator class="assigned"></generator>
        </id>
        <property name="deptName" column="deptName" type="java.lang.String"></property>
        <property name="location" column="location" type="java.lang.String"></property>
    </class>

</hibernate-mapping>

Hibernate:
select
dept0_.deptNo as deptNo5_0_,
dept0_.deptName as deptName5_0_,
dept0_.location as location5_0_
from
project.dept dept0_
where
dept0_.deptNo=?

延迟加载

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.pojo.Dept" table="dept" catalog="project" lazy="true">
        <id name="deptNo" column="deptNo" type="java.lang.Byte">
            <generator class="assigned"></generator>
        </id>
        <property name="deptName" column="deptName" type="java.lang.String"></property>
        <property name="location" column="location" type="java.lang.String"></property>
    </class>

</hibernate-mapping>

org.hibernate.LazyInitializationException: could not initialize proxy - no Session 抛出异常Hibernate类级别采用延迟加载机制load()方法返回的代理对象没有在Session里初始化,所以需要在事务里访问获取对象的属性

  public Dept load(Serializable id) {

        Transaction tx = null;
        Dept dept = new Dept();
        try {
            tx = deptDao.getCurrentSession().beginTransaction();
            dept = deptDao.load(id);
            System.out.println(dept.getDeptNo());
            tx.commit();
        } catch (HibernateException ex) {
            ex.printStackTrace();
            if (tx != null) {
                tx.rollback();
            }
        }
        return dept;
    }

总结
1.当Dept类级别采用立即加载策略,seesion.load()会执行访问dept表的select语句,如果是采用延迟加载机制,只会返回一个Dept代理类的实例,他的deptNo属性是10,其余属性都为null,且不会执行select语句。
2.通过load()方法加载的的延迟状态的Dept代理对象,除了OID,其他属性均为null。通过调用其getDeptName()方法等可以促使Hibernate执行查询,获得数据从而完成代理实例的初始化。
3.调用getDeptNo()方法访问OID属性,不会触发Hibernate初始化代理类实例的行为。而是直接返回Dept代理类实例的OID值,无须查询数据库。
4.如果加载的Dept代理实例的OID在数据库中不存在,Session的load方法不会立刻抛出异常,因为并未真正执行查询语句,只有当Hibernate完成Dept实例的初始化时,才会真正执行查询语句,才会抛出异常。org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.pojo.Dept#11]
5.Dept代理对象的实例只有在当前Session范围才能被初始化,如果在当前Session的生命周期内,应用程序没有完成Dept代理实例的初始化工作,那么Session关闭后,试图访问该Dept代理实例OID以外的属性,将抛出异常org.hibernate.LazyInitializationException: could not initialize proxy - no

get()和load()方法区别

  • 1.无论Dept.hbm.xml文件的<class>元素的lazy属性true还是false,Session的get()方法在类级别总是使用立即加载策略访问数据库的dept表执行select语句,而load()方法如果在<class>元素的lazy属性是false采用的是立即加载,而true采用的是懒加载机制不会访问数据库dept表不会执行select语句。
  • 2.get()方法返回的是一个已经初始化的持久化对象,拥有所有的属性。而在懒加载策略中load()方法返回的是有OIDde的代理对象其他属性都是null只能访问OID,而且需要在Session中初始化,如果不在Session生命周期内初始化会抛出异常org.hibernate.LazyInitializationException: could not initialize proxy - no
  • 3.当加载的Dept对象实例的OID在数据库中不存在,get()方法执行完select语句后返回的是null,而load()方法不会立刻抛出异常,当Dept代理对象被初始化时才会抛出异常org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.pojo.Dept#11]
  • 4.get()和load()方法都会优先查询Session缓存,get()方法如果没有命中一级缓存会直接查询数据库执行SQL语句,load()方法没有命中一级缓存后会查询二级缓存,如果二级缓存没有命中则最后直接查询数据库表执行SQL语句

list()和iterate()方法区别

  • 1.list()和iterate()方法都是不支持懒加载策略,返回的都是集合对象。list()返回List接口集合,iterate()方法返回Iterator接口集合。
  • 2.list()方法返回的List接口集合,获取所有的对象。iterate()方法返回的Iterator接口集合,查询到所有对象的主键值并保存到session缓存中去。如果不在Session中查询集合中的对象就会抛出异常org.hibernate.SessionException: Session is closed!
  • 3.list()是直接根据SQL提交到数据库查询,并获取所有的对象。 当再次查询时,然后是根据SQL提交到数据库中查询,并获取所有对象。list()方法不支持一级缓存。iterate分两步,第一步是根据SQL提交到数据库查询到所有对象的主键值,并保存到session缓存中去。 然后根据主键查询获取到所有的对象保存到sesson中。当再次查询时,直接根据主键值查询缓存中的对象,如果存在的话,不再提交到数据库中重新查询了。iterate()方法支持一级缓存。
  • 4.list()只执行一条SQL语句,iterate至少执行获取所有对象的主键值的SQL语句,若要在Session中访问集合中的对象时会执行1+N条SQL语句。
Hibernate: 
    select
        department0_.deptNo as col_0_0_ 
    from
        project.Department department0_
Hibernate: 
    select
        department0_.deptNo as deptNo0_0_,
        department0_.deptName as deptName0_0_,
        department0_.location as location0_0_ 
    from
        project.Department department0_ 
    where
        department0_.deptNo=?
sales
Hibernate: 
    select
        department0_.deptNo as deptNo0_0_,
        department0_.deptName as deptName0_0_,
        department0_.location as location0_0_ 
    from
        project.Department department0_ 
    where
        department0_.deptNo=?
sales
Hibernate: 
    select
        department0_.deptNo as deptNo0_0_,
        department0_.deptName as deptName0_0_,
        department0_.location as location0_0_ 
    from
        project.Department department0_ 
    where
        department0_.deptNo=?
sales
Hibernate: 
    select
        department0_.deptNo as deptNo0_0_,
        department0_.deptName as deptName0_0_,
        department0_.location as location0_0_ 
    from
        project.Department department0_ 
    where
        department0_.deptNo=?
sales
Hibernate: 
    select
        department0_.deptNo as deptNo0_0_,
        department0_.deptName as deptName0_0_,
        department0_.location as location0_0_ 
    from
        project.Department department0_ 
    where
        department0_.deptNo=?
研发部
Hibernate: 
    select
        department0_.deptNo as deptNo0_0_,
        department0_.deptName as deptName0_0_,
        department0_.location as location0_0_ 
    from
        project.Department department0_ 
    where
        department0_.deptNo=?
研发部

一对多和多对多关联的查询加载策略

立即加载

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.pojo.Department" table="Department" catalog="project">
        <id name="deptNo" type="java.lang.Byte" column="deptNo">
            <generator class="assigned"></generator>
        </id>
        <property name="deptName" type="java.lang.String" column="deptName"></property>
        <property name="location" type="java.lang.String" column="location"></property>
        <set name="emps" cascade="all" inverse="true" lazy="false">
            <key column="deptNo"></key>
            <one-to-many class="com.pojo.Emp"></one-to-many>
        </set>
    </class>

</hibernate-mapping>

Hibernate:
select
department0_.deptNo as deptNo0_0_,
department0_.deptName as deptName0_0_,
department0_.location as location0_0_
from
project.Department department0_
where
department0_.deptNo=?
Hibernate:
select
emps0_.deptNo as deptNo0_1_,
emps0_.empNo as empNo1_,
emps0_.empNo as empNo1_0_,
emps0_.empName as empName1_0_,
emps0_.job as job1_0_,
emps0_.salary as salary1_0_,
emps0_.deptNo as deptNo1_0_
from
project.Emp emps0_
where
emps0_.deptNo=?

当执行Session的get()方法时,对于Dept对象采用类级别的时立即加载策略,对于Dept地域性的emps集合采用一对多级别的立即加载策略,会立即加载一个Dept对象和多个Emp对象。

延迟加载

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.pojo.Department" table="Department" catalog="project">
        <id name="deptNo" type="java.lang.Byte" column="deptNo">
            <generator class="assigned"></generator>
        </id>
        <property name="deptName" type="java.lang.String" column="deptName"></property>
        <property name="location" type="java.lang.String" column="location"></property>
        <set name="emps" cascade="all" inverse="true" lazy="true">
            <key column="deptNo"></key>
            <one-to-many class="com.pojo.Emp"></one-to-many>
        </set>
    </class>

</hibernate-mapping>

Hibernate:
select
department0_.deptNo as deptNo0_0_,
department0_.deptName as deptName0_0_,
department0_.location as location0_0_
from
project.Department department0_
where
department0_.deptNo=?

当执行Session的get()方法时,对于Dept对象采用类级别的时立即加载策略,对于Dept地域性的emps集合采用一对多级别的延迟加载策略,emps属性引用一个没有被初始化集合代理对象,此时emps集合中没有存放任何的Emp对象,只有当emps集合代理对象被初始化时才回会到数据库中查询所有与Dept关联的Emp对象。在会话关闭之前,应用程序第一次访问它时,调用它的iterator(),size(),isEmpty(),contains()方法即可完成初始化,如果不在会话关闭之前初始化就调用emps集合中的对象就会抛出异常org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.pojo.Department.emps, no session or session was closed。

 public Department getDept(Serializable id) {
        Transaction tx = null;
        Department result = null;
        try {
            tx = departmentDao.getCurrentSession().beginTransaction();
            result = departmentDao.get(id);
            Set<Emp> emps=result.getEmps();
            Iterator<Emp> empIterator=emps.iterator();
            while (empIterator.hasNext())
            {
                Emp emp=empIterator.next();
                System.out.println(emp.getEmpName());
            }
            tx.commit();
        } catch (HibernateException ex) {
            ex.printStackTrace();
            if (tx != null) {
                tx.rollback();
            }
        }
        return result;
    }

Hibernate:
select
department0_.deptNo as deptNo0_0_,
department0_.deptName as deptName0_0_,
department0_.location as location0_0_
from
project.Department department0_
where
department0_.deptNo=?
Hibernate:
select
emps0_.deptNo as deptNo0_1_,
emps0_.empNo as empNo1_,
emps0_.empNo as empNo1_0_,
emps0_.empName as empName1_0_,
emps0_.job as job1_0_,
emps0_.salary as salary1_0_,
emps0_.deptNo as deptNo1_0_
from
project.Emp emps0_
where
emps0_.deptNo=?
张三

增强延迟加载

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.pojo.Department" table="Department" catalog="project">
        <id name="deptNo" type="java.lang.Byte" column="deptNo">
            <generator class="assigned"></generator>
        </id>
        <property name="deptName" type="java.lang.String" column="deptName"></property>
        <property name="location" type="java.lang.String" column="location"></property>
        <set name="emps" cascade="all" inverse="true" lazy="extra">
            <key column="deptNo"></key>
            <one-to-many class="com.pojo.Emp"></one-to-many>
        </set>
    </class>

</hibernate-mapping>

增强延迟加载策略能进一步的延迟Dept对象的emps集合代理类实例的初始化时机,当程序第一次访问emps属性的iterator()方法时,会导致emps集合代理类实例的初始化,而当程序第一次访问emps属性的size(),contains()和isEmpty()方法时,Hibernate不会初始化emps集合代理实例,仅通过特定的select语句查询必要的信息select count(empNo) from project.Emp where deptNo =?

public Department getDept(Serializable id) {
        Transaction tx = null;
        Department result = null;
        try {
            tx = departmentDao.getCurrentSession().beginTransaction();
            result = departmentDao.get(id);
            Set<Emp> emps=result.getEmps();
            int size=emps.size();
            System.out.println(size);
            tx.commit();
        } catch (HibernateException ex) {
            ex.printStackTrace();
            if (tx != null) {
                tx.rollback();
            }
        }
        return result;
    }

Hibernate:
select
department0_.deptNo as deptNo0_0_,
department0_.deptName as deptName0_0_,
department0_.location as location0_0_
from
project.Department department0_
where
department0_.deptNo=?
Hibernate:
select
count(empNo)
from
project.Emp
where
deptNo =?
1

配置多对一关联的查询加载策略

延迟加载

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>

    <class name="com.pojo.Emp" table="Emp" catalog="project">
        <id name="empNo" column="empNo" type="java.lang.Integer">
            <generator class="assigned"></generator>
        </id>
        <property name="empName" column="empName" type="java.lang.String"></property>
        <property name="job" column="job" type="java.lang.String"></property>
        <property name="salary" column="salary" type="java.lang.Double"></property>
        <many-to-one name="department" column="deptNo" class="com.pojo.Department" lazy="proxy"></many-to-one>
    </class>

</hibernate-mapping>
 public Emp get(Serializable id)
    {
        Transaction tx=null;
        Emp emp=new Emp();
        try {
            tx=empDao.getCurrentSession().beginTransaction();
            emp=empDao.get(id);
            Department department=emp.getDepartment();//返回Dept代理类实例的引用,OID由emp表的外键值决定
            System.out.println(emp.getDepartment().getDeptName());
            tx.commit();
        }catch (HibernateException ex)
        {
            ex.printStackTrace();
            if(tx!=null)
            {
                tx.rollback();
            }
        }
        return emp;
    }

Hibernate:
select
emp0_.empNo as empNo1_0_,
emp0_.empName as empName1_0_,
emp0_.job as job1_0_,
emp0_.salary as salary1_0_,
emp0_.deptNo as deptNo1_0_
from
project.Emp emp0_
where
emp0_.empNo=?
Hibernate:
select
department0_.deptNo as deptNo0_0_,
department0_.deptName as deptName0_0_,
department0_.location as location0_0_
from
project.Department department0_
where
department0_.deptNo=?
研发部

当执行Session的get()方法时,会立即执行查询Emp对象的select语句,Emp对象的dept属性引用Dept代理类实例,这个代理类的实例的OID由EMP表外键值决定,当执行dept.getDeptName()方法时,Hibernate会初始化Dept代理类实例,执行select语句会从数据库中加载Dept对象。

无代理延迟加载

public Emp get(Serializable id) {
        Transaction tx = null;
        Emp emp = new Emp();
        try {
            tx = empDao.getCurrentSession().beginTransaction();
            emp = empDao.get(id);
            Department department = emp.getDepartment();//返回Dept代理类实例的引用,OID由emp表的外键值决定
            System.out.println(department);
            tx.commit();
        } catch (HibernateException ex) {
            ex.printStackTrace();
            if (tx != null) {
                tx.rollback();
            }
        }
        return emp;
    }

如果对Emp对象的dept属性使用无代理对象延迟加载策略,加载Emp对象的dept属性时null,当执行emp.getDept()时才会执行查询dept表的select语句,从而加载Dept对象。但是当lazy属性为"no-proxy"时,需要在编译期间进行字节码增强操作,否则运行情况和lazy属性时"proxy"时相同,debug运行效果。

字节码增强技术相当于是一把打开运行时JVM的钥匙,利用它可以动态地对运行中的程序做修改,也可以跟踪JVM运行中程序的状态。此外,我们平时使用的动态代理、AOP也与字节码增强密切相关,它们实质上还是利用各种手段生成符合规范的字节码文件。综上所述,掌握字节码增强后可以高效地定位并快速修复一些棘手的问题(如线上性能问题、方法出现不可控的出入参需要紧急加日志等问题),也可以在开发中减少冗余代码,大大提高开发效率。

立即加载

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>

    <class name="com.pojo.Emp" table="Emp" catalog="project">
        <id name="empNo" column="empNo" type="java.lang.Integer">
            <generator class="assigned"></generator>
        </id>
        <property name="empName" column="empName" type="java.lang.String"></property>
        <property name="job" column="job" type="java.lang.String"></property>
        <property name="salary" column="salary" type="java.lang.Double"></property>
        <many-to-one name="department" column="deptNo" class="com.pojo.Department" lazy="false"></many-to-one>
    </class>

</hibernate-mapping>

Hibernate:
select
emp0_.empNo as empNo1_0_,
emp0_.empName as empName1_0_,
emp0_.job as job1_0_,
emp0_.salary as salary1_0_,
emp0_.deptNo as deptNo1_0_
from
project.Emp emp0_
where
emp0_.empNo=?
Hibernate:
select
department0_.deptNo as deptNo0_0_,
department0_.deptName as deptName0_0_,
department0_.location as location0_0_
from
project.Department department0_
where
department0_.deptNo=?

如果对Emp对象的dept属性使用立即加载策略,会立即获取到一个Dept对象。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容

  • 这部分主要是开源Java EE框架方面的内容,包括Hibernate、MyBatis、Spring、Spring ...
    杂货铺老板阅读 1,364评论 0 2
  • 引出 •请思考如下问题? –查询所有员工的每个月工资总和,平均工资? –查询工资最高和最低的工资是多少? –查询公...
    C_cole阅读 7,284评论 0 3
  • ** 延迟加载的好处**1.降低了内存的开销,在用到某些数据的时候才会到数据中进行查询。2.提高了程序运行性能。注...
    我相信你爱过gg阅读 548评论 0 2
  • 本节重点:1、hibernate映射文件2、hibernate核心配置文件3、hibernate核心类 1、hib...
    Vincilovfang阅读 615评论 0 2
  • 5.多表查询 多表查询 目的:从多张表获取数据 前提:进行连接的多张表中有共同的列 等连接 通过两个表具有相同意义...
    乔震阅读 1,220评论 0 0