HQL:Hibernate Query Language,是Hibernate框架中的查询语言,十分接近于SQL语言!以下介绍一些常用的Hql语句:
一、测试类
Classes类:
<span style="font-family:KaiTi_GB2312;font-size:18px;">package com.bjpowernode.hibernate; import java.util.Set; public class Classes { private int id; private String name; private Set students; ...... }</span>
Student类:
<span style="font-family:KaiTi_GB2312;font-size:18px;">package com.bjpowernode.hibernate; import java.util.Date; public class Student { private int id; private String name; private Date createTime; private Classes classes; public Student() { } public Student(int id, String name) { this.id = id; this.name = name; } ...... }</span>
初始化数据类:
<span style="font-family:KaiTi_GB2312;font-size:18px;">package com.bjpowernode.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("2009-07-01","2009-09-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; } }</span>
二、常用的hql语句
2.1,简单属性查询
单一属性:
<span style="font-family:KaiTi_GB2312;font-size:18px;">//返回结果集属性列表,元素类型和实体类中的属性类型一致 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); }</span>多个属性:
<span style="font-family:KaiTi_GB2312;font-size:18px;">//查询多个属性,返回对象数组集合,数组元素的类型与查询的属性类型一致 //数组的长度与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]); }</span>多个属性查询,返回实体对象
<span style="font-family:KaiTi_GB2312;font-size:18px;">//可以使用hql返回Student对象 <span style="color:#ff6666;">//需要提供构造函数</span> 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()); }</span>
2.2,带参数的查询
拼串查询
<span style="font-family:KaiTi_GB2312;font-size:18px;">List students = session.createQuery("select s.id, s.name from Student s where s.name like '%0%'").list();</span>使用?方式传递参数,索引从0开始,值能使用单引号引起来
<span style="font-family:KaiTi_GB2312;font-size:18px;">//方法链编程,建议采用此种方式 List students = session.createQuery("select s.id, s.name from Student s where s.name like ?") .setParameter(0, "%0%") .list();</span>
<span style="font-family:KaiTi_GB2312;font-size:18px;">//采用 ?方式,查询学号为1,2,3,4,5的学生 List students = session.createQuery("select s.id, s.name from Student s where s.id in(?, ?, ?, ?, ?)") .setParameter(0, 1) .setParameter(1, 2) .setParameter(2, 3) .setParameter(3, 4) .setParameter(4, 5) .list();</span>
使用 :参数名的方式传递参数
<span style="font-family:KaiTi_GB2312;font-size:18px;">//可以采用 :参数名 的方式传递参数 List students = session.createQuery("select s.id, s.name from Student s where s.name like :myname") .setParameter("myname", "%0%") .list();</span>
<span style="font-family:KaiTi_GB2312;font-size:18px;">//采用 :参数名 方式,查询学号为1,2,3,4,5的学生 List students = session.createQuery("select s.id, s.name from Student s where s.id in(:ids)") .setParameterList("ids", new Object[]{1, 2, 3, 4, 5}) .list();</span>
2.3,对象导航查询(通过cross join查询,慎重考虑使用)
<span style="font-family:KaiTi_GB2312;font-size:18px;">List students = session.createQuery("from Student s where s.classes.name like '%2%'") .list();</span>
2.4,外置命名查询
实体映射文件中的配置:
<span style="font-family:KaiTi_GB2312;font-size:18px;"><query name="queryStudent"> <![CDATA[ select s from Student s where s.id <? ]]> </query> </span>查询时的调用:
<span style="font-family:KaiTi_GB2312;font-size:18px;">List students = session.getNamedQuery("queryStudent") .setParameter(0, 10) .list();</span>
2.5,分页查询
<span style="font-family:KaiTi_GB2312;font-size:18px;">List students = session.createQuery("from Student") .setFirstResult(1) .setMaxResults(2) .list();</span>setFirstResult:表示第一条记录的索引;setMaxResults:表示每页显示的最大记录数
2.6,原生sql语句查询
<span style="font-family:KaiTi_GB2312;font-size:18px;">List students = session.createSQLQuery("select * from t_student").list();</span>
2.7,统计查询(实际上是调用函数的查询)
<span style="font-family:KaiTi_GB2312;font-size:18px;">String hql = "select c.name, count(s) from Classes c join c.students s group by c.name order by c.name"; List students = session.createQuery(hql).list();</span>
2.8,过滤查询
实体对象映射文件配置:
<pre name="code" class="html"><span style="font-family:KaiTi_GB2312;font-size:18px;"> <class name="com.bjpowernode.hibernate.Student" table="t_student"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <property name="createTime"/> <many-to-one name="classes" column="classesid"/> <span style="color:#ff6666;"><filter name="testFilter" condition="id < :myid"></filter></span> </class> <span style="color:#ff6666;"><filter-def name="testFilter"> <filter-param type="integer" name="myid"/> </filter-def></span></span>
<span style="font-family:KaiTi_GB2312;font-size:18px;">session.enableFilter("testFilter") .setParameter("myid", 10); List students = session.createQuery("from Student").list();</span>
以上就是一些常用的hql语句,下面介绍一下Hibernate中的N+1问题
三、N+1问题
所谓的N+1问题实际上是指Hibernate在查询的时候,发出了N+1条sql语句:
1:发出一条查询id列表的语句
N:根据id发出N条sql语句,加载相关对象
Iterator接收查询结果:
<pre name="code" class="java"><span style="font-family:KaiTi_GB2312;font-size:18px;">Iterator iter = session.createQuery("from Student").iterate(); while (iter.hasNext()) { Student student = (Student)iter.next(); System.out.println(student.getName()); }</span>
控制台打印结果:
解决方案:
每次执行list操作后会将数据放到session的缓存中(一级缓存),所以采用iterate的时候, 首先会发出一条查询id列表的语句,再根据id到缓存中加载相应的数据,如果缓存中存在与之匹配的数据,则不再发出根据id查询的sql语句,直接使用缓存中的数据。
所以,使用list去接收查询的语句,可避免N+1的问题。但如果缓存中存在数据,使用Iterate会比使用List性能高!
注意: 在默认情况下,每次执行list查询实体对象都会发出查询语句,除非配置了查询缓存。虽然一级缓存中存在Student数据,但list不用,所以仍然发出查询语句, 其实list就是只向缓存中放入数据,而不利用缓存中的数据
四、总结
以上就是使用Hibernate框架在查询中需要注意的一些问题。在谈到N+1问题的时候,说到了缓存。那么缓存是提高Hibernate整个框架性能的一个重要因素,下篇博客谈谈Hibernate的缓存机制。它的一级缓存、二级缓存等的开启和管理!