15、HQL簡介及演示數據初始化(1)(hibernate筆記)

主要內容:

  • 簡單屬性查詢
  • 實體對象查詢

一、概述

  • 數據查詢與檢索是Hibernate中的一個兩點。相對其他ORM實現而言,Hibernate提供了靈活多樣的查詢機制。
  • 標準化對象查詢(Criteria Query):以對象的方式進行查詢,將查詢語句封裝為對象操作。優點:可讀性好,符合java程序員的編碼習慣。缺點:不夠成熟,不支持投影(projection)或統計函數(aggregation)。
  • Hibernate語句查詢:是完全面向對象的查詢語句,查詢功能非常強大,具備多態、關聯等特性。Hibernate官方推薦使用HQL進行查詢。
  • Native SQL Queries(原生SQL查詢):直接使用標準SQL語言或跟特定數據庫相關的SQL進行查詢。

注意:在HQL中關鍵字不區分大小寫,但是屬性、實體類名是區分大小寫的。

二、相關示例(工程hibernate_hql

相關映射和實體:
Student.java

private int id;
private String name;
private Date createTime;    
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"/>
        <property name="createTime"/>
        <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>

初始化
InitData.java

package cn.itcast.hibernate;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.hibernate.Session;
public class InitData {

    public static void main(String[] args) {
            Session session = HibernateUtils.getSession();

            try {
                session.beginTransaction();

                for(int i=0; i<10; i++){
                
                    Classes classes = new Classes();
                    classes.setName("班級"+i);
                    session.save(classes);
                    
                    for(int j=0; j<10; j++){
                        Student student = new Student();
                        student.setName("班級"+i+"的學生"+j);
                        student.setCreateTime(randomDate("2008-01-01","2008-03-01"));
                        
                        //在內存中建立由student指向classes的引用
                        student.setClasses(classes);
                        session.save(student);
                    }
                }
                
                for(int i=0; i<5; i++){
                    Classes classes = new Classes();
                    classes.setName("無學生班級"+i);
                    session.save(classes);
                }
                
                for(int i=0; i<10; i++){
                    Student student = new Student();
                    student.setName("無業游民"+i);
                    session.save(student);
                }
                
                session.getTransaction().commit();
            } catch (Exception e) {
                e.printStackTrace();
                session.getTransaction().rollback();
            } finally{
                HibernateUtils.closeSession(session);
            }
        }   
        
        /**
         * 獲取隨機日期
         * @param beginDate 起始日期,格式為:yyyy-MM-dd
         * @param endDate 結束日期,格式為:yyyy-MM-dd
         * @return
         */
        private static Date randomDate(String beginDate,String endDate){
            try {
                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
                Date start = format.parse(beginDate);
                Date end = format.parse(endDate);
                
                if(start.getTime() >= end.getTime()){
                    return null;
                }
                
                long date = random(start.getTime(),end.getTime());
                
                return new Date(date);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
        
        private static long random(long begin,long end){
            long rtn = begin + (long)(Math.random() * (end - begin));
            if(rtn == begin || rtn == end){
                return random(begin,end);
            }
            return rtn;
        }
}

2.1簡單屬性查詢【重點】

  • 單一屬性查詢,返回結果集屬性列表,元素類型和實體類中相應的屬性類型一致。

  • 多個屬性查詢,返回的的集合元素是對象數組,數組元素的類型和相應的屬性在實體中的類型一致,數組的長度取決于與select中屬性的個數。

  • 如果認為返回數組不夠對象化,可以采用HQL動態實例化student對象。

測試:
SimplePropertyQueryTest.java

package cn.itcast.hibernate;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import junit.framework.TestCase;
/**
 * 簡單屬性查詢
 * @author Administrator
 */
public class SimplePropertyQueryTest extends TestCase {
    
    /**
     * 單一屬性查詢
     */
    public void testQuery1() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            
            //返回結果集屬性列表,元素類型和實體類中相應的屬性類型一致
            List students = session.createQuery("select name from Student").list();
            for (Iterator iter=students.iterator(); iter.hasNext();) {
                String name = (String)iter.next();
                System.out.println(name);
            }
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }
}

多個屬性查詢:

//查詢多個屬性,其集合元素是對象數組
//數組元素的類型和對應的屬性在實體類中的類型一致
//數組的長度取決與select中屬性的個數
List students = session.createQuery("select id, name from Student").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
    Object[] obj = (Object[])iter.next();
    System.out.println(obj[0] + "," + obj[1]);
}

返回Student實體對象

//如果認為返回數組不夠對象化,可以采用hql動態實例化Student對象
//此時list中為Student對象集合
List students = session.createQuery("select new Student(id, name) from Student").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
    Student student = (Student)iter.next();
    System.out.println(student.getId() + "," + student.getName());
}

使用別名

//可以使用別名1
List students = session.createQuery("select s.id, s.name from Student s").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
    Object[] obj = (Object[])iter.next();
    System.out.println(obj[0] + "," + obj[1]);
}
----------------------------------------------------------
//可以使用as命名別名
List students = session.createQuery("select s.id, s.name from Student as s").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
    Object[] obj = (Object[])iter.next();
    System.out.println(obj[0] + "," + obj[1]);
}

2.2實體對象查詢【重點】

  • N+1問題,在默認情況下,使用query.iterate查詢,有可能出現N+1問題

  • 所謂的N+1是在查詢的時候發出了N+1條sql語句
    1:首先發出一條查詢語句去查詢對象id列表
    N:根據id列表到緩存中查詢,如果緩存中不存在與之匹配的數據,那么會根據id發出相應的sql語句

  • list和iterate的區別

    • list默認情況下,每次都會發出sql語句,list會向緩存中放數據,但是默認是不利用緩存中的數據
    • iterate默認情況下是會利用緩存,只有在緩存中沒有相應的數據才會發出sql語句去數據庫中查詢,即N+1問題。

測試:
SimpleObjectQueryTest1.java

    public void testQuery1() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            
            //返回Student對象的集合
            //可以忽略select
            List students = session.createQuery("from Student").list();
            for (Iterator iter=students.iterator(); iter.hasNext();) {
                Student student = (Student)iter.next();
                System.out.println(student.getName());
            }
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }

使用別名

//返回Student對象的集合
//可以忽略select
List students = session.createQuery("from Student s").list();
//List students = session.createQuery("from Student as s").list();
//List students = session.createQuery("select s from Student as s").list();
for (Iterator iter=students.iterator(); iter.hasNext();) {
    Student student = (Student)iter.next();
    System.out.println(student.getName());
}

說明:第一種方式和第二種方式差不多,最后一種注意必須使用別名(當我么使用select的時候)。最后注意,不支持
List students = session.createQuery("select * from Student").list();
這種方式,即不支持select * from ....。

SimpleObjectQueryTest2.java

package cn.itcast.hibernate;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import junit.framework.TestCase;
/**
 * 實體對象查詢
 * @author Administrator
 */
public class SimpleObjectQueryTest2 extends TestCase {

    public void testQuery1() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            /**
             * 采用list查詢發出一條查詢語句,取得Student對象數據、
             * Hibernate: select student0_.id as id1_, student0_.name as name1_, 
             * student0_.createTime as createTime1_, student0_.classesid as classesid1_ 
             * from t_student student0_
             * 
             */
            List students = session.createQuery("from Student").list();
            for (Iterator iter=students.iterator(); iter.hasNext();) {
                Student student = (Student)iter.next();
                System.out.println(student.getName());
            }
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }
    
    public void testQuery2() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            /**
             * 出現N+1問題
             * 
             * 1:發出查詢id列表的sql
             *   Hibernate: select student0_.id as col_0_0_ from t_student student0_
             * 
             * N:在依次發出根據id查詢Student對象的sql
             * Hibernate: select student0_.id as id1_0_, student0_.name as name1_0_, 
             * student0_.createTime as createTime1_0_, student0_.classesid as classesid1_0_ 
             * from t_student student0_ where student0_.id=?
             *  
             */
            Iterator iter = session.createQuery("from Student").iterate();
            while(iter.hasNext()) {
                Student student = (Student)iter.next();
                System.out.println(student.getName());
            }
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }   
    
    public void testQuery3() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            
            List students = session.createQuery("from Student").list();
            for (Iterator iter=students.iterator(); iter.hasNext();) {
                Student student = (Student)iter.next();
                System.out.println(student.getName());
            }
            System.out.println("---------------------------------------------");
            
            /**
             * 不會出現N+1問題
             * 
             * 因為list操作已經將Student對象放到了一級緩存中,所以再次使用iterate操作的時候
             * 它首先發出一條查詢id列表的sql,在根據id到緩存中去數據,只有在緩存中找不到相應的
             * 數據時,才會發出sql到數據庫中查詢
             * 
             */
            Iterator iter = session.createQuery("from Student").iterate();
            while(iter.hasNext()) {
                Student student = (Student)iter.next();
                System.out.println(student.getName());
            }
            
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }
    
    public void testQuery4() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            
            List students = session.createQuery("from Student").list();
            for (Iterator iter=students.iterator(); iter.hasNext();) {
                Student student = (Student)iter.next();
                System.out.println(student.getName());
            }
            System.out.println("---------------------------------------------");
            
            /**
             * 再次發出查詢sql
             * 
             * 在默認情況下list每次都會向數據庫發出查詢對象的sql,除非配置查詢緩存,所以下面的list操作
             * 雖然在一級緩存中已經有了對象數據,但list默認情況下不會利用緩存,而再次發出sql
             * 
             * 默認情況下,list會向緩存中放入數據,但不會利用數據
             * 
             */
            students = session.createQuery("from Student").list();
            for (Iterator iter=students.iterator(); iter.hasNext();) {
                Student student = (Student)iter.next();
                System.out.println(student.getName());
            }
            
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }   
}

說明:

  • 1.對于方法一,我們可以看到使用list方法發出一條sql語句將所有的對象都查詢出來。

  • 2.對于方法二,我們使用iterator方法進行查詢,此時查詢就不一樣了,先是發出一條sql語句將所有的id主鍵都查詢出來,然后根據主鍵去找相關的數據,首先在緩存中找,如果緩存中沒有對應的數據,那么就會發出sql語句去數據庫中查詢,于是就出現了N+1問題,因為在后面會發出多條sql語句,這樣對于數據庫的性能損耗是很大的。

  • 3.從方法三中我們也可以看到當我們先使用list查詢出對象之后再使用iterator方法查詢就不會再次發出sql語句,因為iterator方法會首先在緩存中找,而list方法已經將相關數據放在了緩存中,所以iterator方法不會再次發出sql語句,但是如果我們在后面還是使用list方法而不是iterator方法,那么還是會發出查詢語句,從方法四中可以看到,這就說明,iterator方法可以利用緩存,而list方法不會利用緩存。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,400評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,136評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,714評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,452評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,818評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,997評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,552評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,292評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,510評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,721評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,121評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,429評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,235評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,480評論 2 379

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,733評論 18 399
  • Hibernate是一個開放源代碼的對象關系映射框架,它對JDBC進行了非常輕量級的對象封裝,它將POJO與數據庫...
    蘭緣小妖閱讀 1,218評論 1 18
  • 一. Java基礎部分.................................................
    wy_sure閱讀 3,830評論 0 11
  • 這部分主要是開源Java EE框架方面的內容,包括Hibernate、MyBatis、Spring、Spring ...
    雜貨鋪老板閱讀 1,415評論 0 2
  • 作者:夏汐蕊?想看其他作品請點擊這里簡書連載風云錄 【第三十四章】命中已注定, 愛你今生不變(一) 今天,是杜云帆...
    夏汐蕊閱讀 302評論 0 6