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

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,602评论 18 399
  • Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库...
    兰缘小妖阅读 1,198评论 1 18
  • 一. Java基础部分.................................................
    wy_sure阅读 3,806评论 0 11
  • 这部分主要是开源Java EE框架方面的内容,包括Hibernate、MyBatis、Spring、Spring ...
    杂货铺老板阅读 1,357评论 0 2
  • 作者:夏汐蕊☞想看其他作品请点击这里简书连载风云录 【第三十四章】命中已注定, 爱你今生不变(一) 今天,是杜云帆...
    夏汐蕊阅读 290评论 0 6