HQL查询
一 介绍
1.HQL:Hibernate Query Language,是一种完全面向对象的查询语言。
使用Hibernate有多重查询方式可供选择:hibernate的HQL查询,也可以使用条件查询,甚至使用原生的SQL查询语句。Hibernate还提供了一种数据过滤功能,这些都用于删选目标数据。
2.查询步骤:
(1)获取Hibernate Session对象
(2)编写HQL语句
(3)以HQL语句为参数,调用Session的createQuery()方法创建查询对象
(4)如果HQL语句包含参数,则调用Query的setXxx()方法为参数赋值
(5)调用Query对象的list()或uniqueResult()方法返回查询结果列表
例如:假设两个关联实体Person和MyEvent之间存在中N--N关联关系,具体的查询如下:

private class HqlQuery{
	public static void main(String[] args) throws Exception{
	HqlQuery mgr=new HqlQuery();
	mgr.findPersons();
	} 
	private void findPersons(){
		Session sess=HibernateUtil.currentSession();
		Transaction tx=sess.beginTransaction();
		//以HQL语句创建Query对象
		List p1=sess.creatQuery("select distinct p from Person p "+
		"join p.myEvents where title= :eventTitle")
		.setString("eventTitle","a simple title");
		.list();
		//遍历查询的全部结果
		for(Object ele:p1){
			Person p=(Person)ele;
			System.out.println(p.getName());
		}
		tx.commit();
		HibernateUtil.closeSession();
	}
}


注意:Query对象可以连续多次为HQL参数赋值,因为setXxx()方法返回的是Query本身,最后调用list()方法返回查询到的全部结果。Query还包含如下两个方法:
setFirstResult(int firstResult):设置返回的结果集从第几条记录开始
setMaxResult(int maxResults):设置本次查询返回的结果数目
HQL语句本身不区分大小写,但是HQL语句中所使用的包名、类名、实例名、属性名都区分大小写
二 具体使用
1.from子句
from Person 或者是 from Person as p
as关键字是可选的,但是为了保留可读性,建议保留。
from后还可以跟多个持久化类,此时将产生一个笛卡尔积或跨表的连接,但是这种做法并不经常使用,因为通常在做跨表的连接时,可以考虑使用隐式连接或显式连接,而不是在from后紧跟多个表名。
2.关联和连接
当程序需要从多个数据表中取得数据时,SQL语句会考虑使用多表连接查询。Hibernate使用关联映射处理底层数据表之间的连接,一旦提供了正确的关联映射后,当程序通过Hibernate进行持久化访问时,将可利用Hibernate的关联来进行连接。
HQL支持两种关联连接(join):隐式和显式
隐式连接不使用join关键字,使用英文点号.来隐式连接关联实体,如:from Person p where p.myEvents.title>:title
显式连接需要使用xxx join关键字,如:from Person p inner join p.myEvents event where event.happenDate<:endDate
主要有以下几种连接方式:
inner join(内连接),简写为join
left outer join(左外连接),简写为left join
right outer join(右外连接),简写为right join
full join(全连接),并不常用
使用显式连接时,还可以通过with关键字提供额外的连接条件,如:
from Person p inner join p.myEvents event with p.id>event.id where event.happenDate<:endDate
隐式连接底层将转换成SQL99的交叉连接,显式连接底层将转换成SQL99的inner join,left join,right join等等。
注意:隐式连接和显式连接查询后返回的结果不同
在上述from Person p where p.myEvents.title>:title例子中,使用隐式连接查询返回的结果是多个被查询实体组成的集合,也就是Person对象组成的集合。
而rom Person p inner join p.myEvents event with p.id>event.id where event.happenDate<:endDate例子中,使用显式连接查询返回的结果也是集合,但是集合元素是被查询的持久化对象、所有被关联的持久化对象所组成的数组。该例中同时选择了person和event表中的所有数据列,查询得到的结果集的每条记录既包含了Person实体的全部属性,也包含了MyEvent实体的全部属性。
Hibernate3.2.2以前的版本中,会对所有的关联实体自动使用隐式连接,如:from Person p where p.myEvents.title =:eventTitle,但是在这以后,如果myEvents是一个集合,就会报错:QueryException,要改写为:
from Person p inner join p.myEvents e where e.title =:eventTitle
如果只想获取Person组成的集合,应改为:select p from Person p inner join p.myEvents e where e.title =:eventTitle,但是这么做会产生多个完全相同的Person对象,因此要改为:select distinct p from Person p inner join p.myEvents e where e.title =:eventTitle
也就是说,对于Hibernate3.2.3以后的版本,如果关联实体是单个实体或单个的组件属性,就可以使用隐式连接;当关联实体是集合,则必须使用xxx join来显式连接关联实体或组件。
3.select子句
select子句用于选择指定的属性或直接选择某个实体,选择的属性必须是from后持久化类包含的属性,如:
select p.name from Person as p
如果select后由多个选项,那么每个集合元素就是选择出的多项组成的数组,如:
select p.name, p from Person as p
执行该HQL语句得到的集合元素就是类似于[String,Person]结构的数组
也可以将选择出的属性存入一个List对象中,如:
select new List(p.name, p.address) from Person as p
也可以将选择出的属性直接封装成对象,如:
select new ClassTest(p.name,p.address) from Person as p
前提是ClassTest支持p.name,p.address的构造器
也可以给选中的表达式命名别名,如:
select p.name as personName from Person as p
这种做法与new map使用更为普遍:
select new map(p.name as personName) from Person p
执行上面的HQL语句返回的结果是集合,该Map对象以personName为key,实际的值为value。
4.多态查询
from后面跟持久化类名,不仅会查询出该持久化类的全部实例,还会查询出该类的子类的全部实例,如:
from Person as p
该查询语句不仅会查询出Person的全部实例,还会查出其子类,如Teacher的全部实例。
5.where子句
where子句用来筛选选中的结果,缩小选择的范围。
select Person where name like 'tom%'
select Person as p where p.name like "tom%"
以上两种写法的效果是一样的。
只要没有集合属性,使用隐式连接多个数据库会相当方便,不会感受到多表连接查询的繁琐。
6.表达式
where子句后支持的运算符非常丰富,允许使用大部分SQL支持的表达式,包括:
数学运算符:+ - * /等
二进制比较运算符:= >= <= <> != like
逻辑运算符:and or not 等
in not in between is null is not null is emtpy is not empty等等
字符串连接符: value1 || value2 concat(value1,value2)
时间操作函数: current_date() current_time() current_timestamp() second() minute() hour() day() month() year()
EJB-QL3.0所支持的函数或操作:substring() trim() lower() upper() length()等等
where子句还支持以下的特殊关键字用法:
(1)HQL index()函数:作用于join的有序集合的别名
(2)HQL 函数,把集合作为参数:size(),minelement(),maxelement(),minindex(),maxindex()
(3)in和between...and的用法:select DemesticCat cat where cat.name between 'A' and 'B'
select DemesticCat cat where cat.name in ('Foo','Bar','Baz')
当然,也支持not between和not in的用法
(4)is null 和is not null用来测试空值
(5)size关键字用来返回一个集合的大小
from Cat cat where cat.kittens.size>0或者from Cat cat where size(cat.kittens)>0
(6)对于有序集合,还可以使用minindex(),maxindex()函数代表最小与最大的索引序数,如:
from Calendar cal where maxindex(order.items) >100
7.order by子句
查询返回的集合可以根据类或组件属性的任何属性进行排序,如:
from Person as p order by p.name,p.age
可以使用asc或desc关键字指定升序或降序的排序规则,如:
from Person as p order by p.name asc,p.age desc
8.group by子句
返回聚集值的查询可以对持久化类或组件属性的属性进行分组,分组使用group by子句,如:
select cat.color,sum(cat.weight),count(cat)
from Cat cat
group by cat.color
注意:出现在select后的属性,要么出现在聚集函数中,要么出现在group by的属性列表中。
having子句用于对分组进行过滤,如:
select cat from Cat cat
join cat.kittens kitten
group by cat
having avg(kitten.weight)>100
order by count(kitten) asc,sum(kitten.weight) desc
9.子查询
如果底层数据库支持子查询,则可以在HQL语句中使用子查询,如:
from Cat as fatcat
where fatcat.weight>(select avg(cat.weight) from DomesticCat cat);