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




    
        
            
        
        
        
        
    

Classes.hbm.xml




    
        
            
        
        
        
            
            
        
    

初始化
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方法不会利用缓存。

你可能感兴趣的:(15、HQL简介及演示数据初始化(1)(hibernate笔记))