一、HQL查询的from子句
from是最简单的语句,也是最基本的HQL语句。from关键字后紧跟持久化类的类名。
例如:
from Person
推荐为Person持久化类的每个实例起别名,例如:
from Person as p
p作为Person的实例的别名,因此也应该遵守Java的命名规则:第一个单词的首字母小写,后面每个单词的首字母大写。
命名别名时,as关键字是可选的,但为了增加可读性,建议保留。
from 后还可同时出现多个持久化类,此时将产生一个笛卡尔积或跨表的连接,但实际上这种用法很少使用,因为通常我们可能需要使用跨表的连接,此时可以考虑使用隐式连接或显示连接,而不是直接在from后紧跟多个表名。
二、关联和连接
隐式连接形式不适用join关键字,使用英文点号(.)来隐式连接来关联实体,而Hibernate底层将自动进行关联查询。如下HQL语句:
from Person p where p.myEvent.title > :title
上面的p.myEvent属性的实质是一个持久化实体,因此Hibernate底层隐式地自动进行连接查询。
显示连接则需要使用xxx join关键字,例如如下语句:
//使用显示连接
from Person p
inner join p.myEvent event
where event.happenDate < :endDate
使用显示连接时,可以为相关联的实体,甚至是关联集合中的全部元素指定一个别名。
Hibernate支持的HQL连接类型,可使用如下几种连接方式:
inner join(内连接),可简写成join。
left outer join(左外连接),可简写成left join。
right outer join(右外连接),可简写成right join。
full join(全连接),并不常用。
使用显示连接时,还可通过HQL的with关键字来提供额外的连接条件,例如如下HQL语句:
from Person p
inner join p.myEvent event
with p.id > event.id
where event.happenDate < :endDate
三、隐式连接和显示连接有如下两点区别:
(1)隐式连接底层转换成SQL92的内连接,显示连接层将转换成SQL99的多表连接。
(2)隐式连接和显示连接查询后返回的结果不同。
当HQL语句中省略select关键字时,使用隐式连接查询返回的结果是多个被查询实体组成的集合。
当HQL语句中省略select关键字时,使用显示连接查询返回的结果也是集合,但集合元素是被查询持久化对象、所有被关联的持久化对象所组成的数组。
使用fetch关键字时,有如下几个注意点:
程序里希望预加载那些原本应延迟加载的属性,则可以通过fetch all properties来强制Hibernate立即抓取这些属性。例如:
from Document fetch all properties order by name
四、HQL查询的select子句
(1)select子句用于选择指定的属性或直接选择某个实体,当然select选择的属性必须是from后持久化类包含的属性。
例如:
select p.name from Person as p
(2)select可以选择任意属性,即不仅可以选择持久化类的直接属性,还可以选择组件属性包含的属性。例如:
select p.name.firstName from Person as p
(3)在特殊情况下,如果select后只有一项(包括持久化实例或属性),则查询得到的集合元素就是该持久化实例或属性。
(4)如果select后有多个项,则每个集合就是选择出的多项组成的数组。
例如:
select p.name,p from Person as p
执行该HQL语句得到的集合元素时类似于[String,Person]结构的数组,其中第一个元素时Person实例的name属性,第二个元素时Person实例。
注意:即使select后的列表项选出某个持久化类的全部属性,这些属性依然是属性,Hibernate不会将这些属性封装成对象。只有在select后的列表里给出持久化类的别名(其实就是实例名),Hibernate才会将该项封装成一个持久化实体。
(5)select支持将选择出的属性存入一个List对象中。
例如:
select new list(p.name, p.address) from Person as p
例如:
select new ClassTest(p,name, p.address) from Person as p;
而ClassTest必须有如下的构造器:ClassTest(String s1, String s2)
(7)select还支持给选中的表达式命名别名。例如:
select p.name as personName from Person as p
这种用法与new map 结合使用更普遍。例如:
select new map(p.name as personName) from Person as p
执行上面的HQL语句返回的结果是集合,其中集合元素时Map对象,以personName作为Map的key,实际选出的值作为Map的value。
五、HQL查询的聚集函数
HQL支持的聚集函数与SQL的完全相同:avg,count,max,min,sum。
例如,如下的HQL语句:
select count(*) from Person
select max(p.page) from Person as p
select子句还支持字符串连接符、算术连接符,以及SQL函数。例如:
select p.name||""||p.address from Person as p
select子句也支持使用distinct和all关键字,此时的效果与SQL的效果完全相同。
六、多态查询
如下面的查询语句:from Person as p
如下面的查询语句:from Named as n
注意:上面最后一条查询,需要多个SQL SELECT 语句,因此无法使用order by子句对结果集排序,从而不允许对这些查询结果使用Query.scroll()方法。
七、HQL查询的where子句
(1)where子句用于筛选选中的结果,缩小选择的范围。如果没有为持久化实例命名别名,则可以直接使用属性名来引用属性。
如下面两条HQL语句:
from Person where name like "tom%"
form Person as p where p.name like "tom%"
(2)复合属性表达式加强了where子句的功能,例如,如下的HQL语句:
from Cat cat where cat.mate.name like "kit%"
上面语句被翻译成以下含有内连接的SQL查询:
select * from cat_table as table1 cat_table as table2
where table1.mate = table2.mate
and table1.name like '''kit%'"
实际上这种用法使用了隐式连接查询,从Hibernate3.2.3之后,只有当cat,mate属性引用的是普通组件属性或者单独的关联实体时才可接着在后面使用点好(.)来引用mate属性,如cat,mate.name;如果cat,mate是集合属性,Hibernate3.2.3以后的版本不支持这种用法。
"="号不仅可以被用来比较属性的值,也可以用来比较实例。
select cat,mate from Cat cat, Cat mate where cat.mate = mate
(3)在进行多态持久化的情况下,class关键字用来存取一个实例的鉴别值。嵌入where子句的Java类名,将被作为该类的鉴别值。
//执行多态查询时,默认会选出Cat及其所有子类的实例
//在如下HQL语句中,将只选出DomesticCat类的实例
from Cat cat where cat.class = DomesticCat
(4)当where子句中的运算符只支持基本类型或者字符串时,where子句中的属性表达式必须以基本类型或者字符串结尾,不要使用组件类型属性结尾,例如Account和Person属性,而Person有Name属性,Name属性有firstName属性。
如下所示:
//firstName是字符串
from Account as a where a.person.name.firstName like "dd%"
//下面是错误实例
from Account as a where a.person.name like "dd%"
八、表达式
1、HQL的功能非常丰富,where子句后支持的运算符,不仅包括SQL的运算符,也包括EJB-QL的运算符等。
where子句中允许使用大部分SQL支持的表达式,包括如下种类:
(1)字符串连接:如value1||value2,或使用字符串连接函数concat(value1, value2)
(2)简单的case,case...when...then...else...end和case,case when...then...else...end等。
(3)时间操作函数:current_date()、current_time、year()等。
(4)EJB-QL3.0的函数:substring()、trim()、abs()、sqrt()等。
(5)可在where子句中使用SQL常量。
(6)在HQL语句中使用Java中的public static final类型的常量,例如Color.RED
(7)HQL语句支持使用英文问号(?)作为参数占位符,这与JDBC的参数占位符一致;也使用命名参数占位符号,方法在参数名前加英文冒号(:),例如:start_date等。
(8)如果底层数据库支持单行函数,则HQL语句也完全可以支持。
(9)支持数据库的类型转换函数,如cast(... as ...),第二个参数是Hibernate的类型名,或者extract(... from ,,,),前提是底层数据库支持ANSI cast()和extract()。
2、如果在Hibernate配置文件中进行如下声明:
<property name="hibernate.query.substitutions">true 1,false 0</property>
上面的声明表明:HQL转换SQL语句时,将使用字符1和0来取代关键字true和false,然后将可以在表达式中使用布尔表达式。
3、有用的elements()和indices函数,用于返回指定集合的所有元素和所有索引。
4、在where子句中,有序集合(数组、List集合、Map对象)的元素可以通过【】运算符来访问。如下:
from Order order where order.items[0].id=1234
在【】中的表达式甚至可以是一个算式表达式。
5、结构变量:size、elements、indices等,只能在where子句中使用。
九、order by子句
查询返回的集合根据类或组件属性的任何属性进行排序。例如:
from Person as p order by p.name,p.age
还可以使用as或desc关键字指定升序或降序的排序规则。例如:
from Person as p order by p.name asc,p.age desc
如果没有指定排序规则,默认采用升序规则。
十、group by子句
返回聚集值的查询可以对持久化类或组件属性的进行分组,分组使用group by子句。看下面的HQL查询语句:
select cat,color,sum(cat.weight),count(cat) from Cat cat group by cat.color
其规则类似于SQL规则。
having子句用于对分组进行过滤,如下所示:
select cat,color,sum(cat.weight),count(cat) from Cat cat group by cat.color having cat.color in (eg.Color.TABBY,eg.Color.BLACK)
注意:group by子句与order by 子句中都不能包含算术表达式
十一、子查询
(1)如果底层数据库支持子查询,则可以在HQL语句中使用子查询。如下:
from Cat as fatcat where fatcat.weight > (select avg(cat.weight) from DomesticCat cat)
(2)如果子查询是多行结果集,则应该使用多行运算符。如下:
from Cat as cat where not(cat.name, cat.color) in (select cat.name, cat.color from DomesticCat cat)
(3)SQL语法中子查询还可以出现在select子句之后,HQL也支持这种用法,看如下HQL语句:
select cat.id,(select max(kit.weight) from cat.kitten kit)
from Cat as cat
注意:HQL子查询只可以在select子句或者where子句中出现。
十二、命名查询
HQL支持将查询所用的HQL语句放入配置文件中,而不是代码中。
在Hibernate映射文件的<hibernate-mapping/>元素中使用<query/>子元素来定义命名查询,使用<query/>元素只需要指定一个name属性,指定该命名查询的名字。该元素的内容就是命名查询的HQL语句。如下配置文件片段:
<query name="myNameQuery">
</query>
配置好后,通过Session提供的一个getNameQuery(String name)方法,该方法用于创建一个Query对象,剩下的操作与普通HQL完全一样。如下所示:
List p1 = sess.getNamedQuery("myNameQuery").setInteger(0,20).list()
十三、条件查询
条件查询通过如下3个类完成:
Criteria:代表一次查询。
Criterion:代表一个查询条件。
Restrictions:产生查询条件的工具类。
执行条件查询的步骤如下:
(1)获得Hibernate的Session对象。
(2)以Session对象创建Criteria对象。
(3)使用Restrictions的静态方法创建Criterion查询条件。
(4)向Criteria查询中体检Criterion查询条件。
(5)执行Criteria的list等方法返回结果集。
代码片段如下:
List l = session.createCriteria(Student.class)
在条件查询中,Criteria接口代表依次查询,该查询本身不具备任何的数据筛选功能,Session调用createCriterial(Class clazz)方法对某个持久化类创建条件查询实例。
Criteria对象不具备任何的数据筛选功能,但程序可以通过向Criteria对象中组合多个Criterion(每个Criterion对象代表一个过滤条件)即可实现数据过滤了。
Criterion接口代表一个查询条件,该查询条件由Restrictions负责产生。Restrictions是专门用于产生查询条件的工具类,它的方法大部分都是静态方法,常用的方法如下:
(1)static Criteion allEq(Map propertyNameValues):判断指定属性(由Map参数的key指定)和指定值(由Map参数的value指定)是否完全相等。
(2)static Criterion between(String propertyName, Object lo, Object hi):判断属性值在某个指范围之内。
十四、关联和动态关联
代码片段如下:
List l = session.createCriteria(Student.class)
上面的代码表示建立Person类的条件查询,第一个查询条件是直接过滤Person的属性。第二个查询条件则过滤Person的关联实体的属性,其中enrolments是Person类的关联实体,而semester则是Enrolment类的属性。值得注意的是,返回的并不是Enrolment对象,而是Person对象的集合。
注意:使用关联类的条件查询,依然是查询原有持久化类的实例,而不是查询被关联类的实例。
可使用条件查询支持的替换形态,将上面查询代码替换成如下形式:
List l = session.createCriteria(Student.class)
createAlias()方法并不创建一个新的Criteria实例,它只是给关联实体(包含集合里包含的关联实体)起一个别名,让后面过滤条件可根据关联实体进行筛选。
在默认情况下,条件查询将根据映射文件指定的延迟加载策略来加载关联实体,如果希望在条件查询中改变延迟加载策略(就像在HQL查询中使用fetch关键字一样),那就可通过Creteria的setFetchMode()方法来实现,该方法接受一个FetchMode参数。
FetchMode里有几个常量,如下:
DEFAULT:使用配置文件制定的延迟加载策略处理。
JOIN:使用外连接,预初始化所有关联实体。
SELECT:启用延迟加载,系统将使用单独的select语句来初始化关联实体。只有当个真正访问关联实体的时候,才会执行第二条select语句。
初始化Student对象时,也可以初始化Student关联的Enrolment实体,实体使用如下代码:
List l = session.createCriteria(Student.class)