Hibernate查詢加載策略

引言:當(dāng)Hibernate查詢部門對象時,立即查詢并加載與之的員工對象,這查詢策略是立即加載策略。立即加載存在兩大不足:會執(zhí)行不必要的查詢語句,影響性能??赡軙虞d大量不需要的對象,增加系統(tǒng)開銷,浪費內(nèi)存空間。Hibernate提供了延遲加載策略,延遲加載策略能避免加載應(yīng)用程序不需要訪問的關(guān)聯(lián)對象。

延遲加載級別

  • 類級別:<class>元素中l(wèi)azy屬性可選"true"延遲加載和"false"立即加載,默認(rèn)是"true"延遲加載。
  • 一對多和多對多級聯(lián)級別:<set>元素中l(wèi)azy屬性可選true延遲加載,extra增強延遲加載,false立即加載。默認(rèn)值true延遲加載
  • 多對一關(guān)聯(lián)級別:<many-to-one>元素中l(wèi)azy屬性可選proxy延遲加載,no-proxy無代理延遲加載和false立即加載,默認(rèn)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里初始化,所以需要在事務(wù)里訪問獲取對象的屬性

  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;
    }

總結(jié)
1.當(dāng)Dept類級別采用立即加載策略,seesion.load()會執(zhí)行訪問dept表的select語句,如果是采用延遲加載機制,只會返回一個Dept代理類的實例,他的deptNo屬性是10,其余屬性都為null,且不會執(zhí)行select語句。
2.通過load()方法加載的的延遲狀態(tài)的Dept代理對象,除了OID,其他屬性均為null。通過調(diào)用其getDeptName()方法等可以促使Hibernate執(zhí)行查詢,獲得數(shù)據(jù)從而完成代理實例的初始化。
3.調(diào)用getDeptNo()方法訪問OID屬性,不會觸發(fā)Hibernate初始化代理類實例的行為。而是直接返回Dept代理類實例的OID值,無須查詢數(shù)據(jù)庫。
4.如果加載的Dept代理實例的OID在數(shù)據(jù)庫中不存在,Session的load方法不會立刻拋出異常,因為并未真正執(zhí)行查詢語句,只有當(dāng)Hibernate完成Dept實例的初始化時,才會真正執(zhí)行查詢語句,才會拋出異常。org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.pojo.Dept#11]
5.Dept代理對象的實例只有在當(dāng)前Session范圍才能被初始化,如果在當(dāng)前Session的生命周期內(nèi),應(yīng)用程序沒有完成Dept代理實例的初始化工作,那么Session關(guān)閉后,試圖訪問該Dept代理實例OID以外的屬性,將拋出異常org.hibernate.LazyInitializationException: could not initialize proxy - no

get()和load()方法區(qū)別

  • 1.無論Dept.hbm.xml文件的<class>元素的lazy屬性true還是false,Session的get()方法在類級別總是使用立即加載策略訪問數(shù)據(jù)庫的dept表執(zhí)行select語句,而load()方法如果在<class>元素的lazy屬性是false采用的是立即加載,而true采用的是懶加載機制不會訪問數(shù)據(jù)庫dept表不會執(zhí)行select語句。
  • 2.get()方法返回的是一個已經(jīng)初始化的持久化對象,擁有所有的屬性。而在懶加載策略中l(wèi)oad()方法返回的是有OIDde的代理對象其他屬性都是null只能訪問OID,而且需要在Session中初始化,如果不在Session生命周期內(nèi)初始化會拋出異常org.hibernate.LazyInitializationException: could not initialize proxy - no
  • 3.當(dāng)加載的Dept對象實例的OID在數(shù)據(jù)庫中不存在,get()方法執(zhí)行完select語句后返回的是null,而load()方法不會立刻拋出異常,當(dāng)Dept代理對象被初始化時才會拋出異常org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.pojo.Dept#11]
  • 4.get()和load()方法都會優(yōu)先查詢Session緩存,get()方法如果沒有命中一級緩存會直接查詢數(shù)據(jù)庫執(zhí)行SQL語句,load()方法沒有命中一級緩存后會查詢二級緩存,如果二級緩存沒有命中則最后直接查詢數(shù)據(jù)庫表執(zhí)行SQL語句

list()和iterate()方法區(qū)別

  • 1.list()和iterate()方法都是不支持懶加載策略,返回的都是集合對象。list()返回List接口集合,iterate()方法返回Iterator接口集合。
  • 2.list()方法返回的List接口集合,獲取所有的對象。iterate()方法返回的Iterator接口集合,查詢到所有對象的主鍵值并保存到session緩存中去。如果不在Session中查詢集合中的對象就會拋出異常org.hibernate.SessionException: Session is closed!
  • 3.list()是直接根據(jù)SQL提交到數(shù)據(jù)庫查詢,并獲取所有的對象。 當(dāng)再次查詢時,然后是根據(jù)SQL提交到數(shù)據(jù)庫中查詢,并獲取所有對象。list()方法不支持一級緩存。iterate分兩步,第一步是根據(jù)SQL提交到數(shù)據(jù)庫查詢到所有對象的主鍵值,并保存到session緩存中去。 然后根據(jù)主鍵查詢獲取到所有的對象保存到sesson中。當(dāng)再次查詢時,直接根據(jù)主鍵值查詢緩存中的對象,如果存在的話,不再提交到數(shù)據(jù)庫中重新查詢了。iterate()方法支持一級緩存。
  • 4.list()只執(zhí)行一條SQL語句,iterate至少執(zhí)行獲取所有對象的主鍵值的SQL語句,若要在Session中訪問集合中的對象時會執(zhí)行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=?
研發(fā)部
Hibernate: 
    select
        department0_.deptNo as deptNo0_0_,
        department0_.deptName as deptName0_0_,
        department0_.location as location0_0_ 
    from
        project.Department department0_ 
    where
        department0_.deptNo=?
研發(fā)部

一對多和多對多關(guān)聯(lián)的查詢加載策略

立即加載

<?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=?

當(dāng)執(zhí)行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=?

當(dāng)執(zhí)行Session的get()方法時,對于Dept對象采用類級別的時立即加載策略,對于Dept地域性的emps集合采用一對多級別的延遲加載策略,emps屬性引用一個沒有被初始化集合代理對象,此時emps集合中沒有存放任何的Emp對象,只有當(dāng)emps集合代理對象被初始化時才回會到數(shù)據(jù)庫中查詢所有與Dept關(guān)聯(lián)的Emp對象。在會話關(guān)閉之前,應(yīng)用程序第一次訪問它時,調(diào)用它的iterator(),size(),isEmpty(),contains()方法即可完成初始化,如果不在會話關(guān)閉之前初始化就調(diào)用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集合代理類實例的初始化時機,當(dāng)程序第一次訪問emps屬性的iterator()方法時,會導(dǎo)致emps集合代理類實例的初始化,而當(dāng)程序第一次訪問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

配置多對一關(guān)聯(lián)的查詢加載策略

延遲加載

<?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=?
研發(fā)部

當(dāng)執(zhí)行Session的get()方法時,會立即執(zhí)行查詢Emp對象的select語句,Emp對象的dept屬性引用Dept代理類實例,這個代理類的實例的OID由EMP表外鍵值決定,當(dāng)執(zhí)行dept.getDeptName()方法時,Hibernate會初始化Dept代理類實例,執(zhí)行select語句會從數(shù)據(jù)庫中加載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,當(dāng)執(zhí)行emp.getDept()時才會執(zhí)行查詢dept表的select語句,從而加載Dept對象。但是當(dāng)lazy屬性為"no-proxy"時,需要在編譯期間進行字節(jié)碼增強操作,否則運行情況和lazy屬性時"proxy"時相同,debug運行效果。

字節(jié)碼增強技術(shù)相當(dāng)于是一把打開運行時JVM的鑰匙,利用它可以動態(tài)地對運行中的程序做修改,也可以跟蹤JVM運行中程序的狀態(tài)。此外,我們平時使用的動態(tài)代理、AOP也與字節(jié)碼增強密切相關(guān),它們實質(zhì)上還是利用各種手段生成符合規(guī)范的字節(jié)碼文件。綜上所述,掌握字節(jié)碼增強后可以高效地定位并快速修復(fù)一些棘手的問題(如線上性能問題、方法出現(xiàn)不可控的出入?yún)⑿枰o急加日志等問題),也可以在開發(fā)中減少冗余代碼,大大提高開發(fā)效率。

立即加載

<?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對象。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容