1030 lines
41 KiB
XML
1030 lines
41 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<chapter id="queryhql">
|
||
<title>HQL: Hibernate查询语言</title>
|
||
<para>
|
||
Hibernate配备了一种非常强大的查询语言,这种语言看上去很像SQL。但是不要被语法结构
|
||
上的相似所迷惑,HQL是非常有意识的被设计为完全面向对象的查询,它可以理解如继承、多态
|
||
和关联之类的概念。
|
||
</para>
|
||
|
||
<sect1 id="queryhql-casesensitivity">
|
||
<title>大小写敏感性问题</title>
|
||
|
||
<para>
|
||
除了Java类与属性的名称外,查询语句对大小写并不敏感。
|
||
所以 <literal>SeLeCT</literal> 与
|
||
<literal>sELEct</literal> 以及
|
||
<literal>SELECT</literal> 是相同的,但是
|
||
<literal>org.hibernate.eg.FOO</literal> 并不等价于
|
||
<literal>org.hibernate.eg.Foo</literal> 并且
|
||
<literal>foo.barSet</literal> 也不等价于
|
||
<literal>foo.BARSET</literal>。
|
||
</para>
|
||
|
||
<para>
|
||
本手册中的HQL关键字将使用小写字母. 很多用户发现使用完全大写的关键字会使查询语句
|
||
的可读性更强, 但我们发现,当把查询语句嵌入到Java语句中的时候使用大写关键字比较难看。
|
||
</para>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="queryhql-from">
|
||
<title>from子句</title>
|
||
|
||
<para>
|
||
Hibernate中最简单的查询语句的形式如下:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from eg.Cat]]></programlisting>
|
||
|
||
<para>
|
||
该子句简单的返回<literal>eg.Cat</literal>类的所有实例。
|
||
通常我们不需要使用类的全限定名, 因为 <literal>auto-import</literal>(自动引入)
|
||
是缺省的情况。 所以我们几乎只使用如下的简单写法:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Cat]]></programlisting>
|
||
|
||
<para>
|
||
大多数情况下, 你需要指定一个<emphasis>别名</emphasis>, 原因是你可能需要
|
||
在查询语句的其它部分引用到<literal>Cat</literal>
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Cat as cat]]></programlisting>
|
||
|
||
<para>
|
||
这个语句把别名<literal>cat</literal>指定给类<literal>Cat</literal>
|
||
的实例, 这样我们就可以在随后的查询中使用此别名了。 关键字<literal>as</literal>
|
||
是可选的,我们也可以这样写:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Cat cat]]></programlisting>
|
||
|
||
<para>
|
||
子句中可以同时出现多个类, 其查询结果是产生一个笛卡儿积或产生跨表的连接。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Formula, Parameter]]></programlisting>
|
||
<programlisting><![CDATA[from Formula as form, Parameter as param]]></programlisting>
|
||
|
||
<para>
|
||
查询语句中别名的开头部分小写被认为是实践中的好习惯,
|
||
这样做与Java变量的命名标准保持了一致
|
||
(比如,<literal>domesticCat</literal>)。
|
||
</para>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="queryhql-joins" revision="1">
|
||
<title>关联(Association)与连接(Join)</title>
|
||
|
||
<para>
|
||
我们也可以为相关联的实体甚至是对一个集合中的全部元素指定一个别名, 这时要使用关键字<literal>join</literal>。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Cat as cat
|
||
inner join cat.mate as mate
|
||
left outer join cat.kittens as kitten]]></programlisting>
|
||
|
||
<programlisting><![CDATA[from Cat as cat left join cat.mate.kittens as kittens]]></programlisting>
|
||
|
||
<programlisting><![CDATA[from Formula form full join form.parameter param]]></programlisting>
|
||
|
||
<para>
|
||
受支持的连接类型是从ANSI SQL中借鉴来的。
|
||
</para>
|
||
|
||
<itemizedlist spacing="compact">
|
||
<listitem>
|
||
<para>
|
||
<literal>inner join</literal>(内连接)
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
<literal>left outer join</literal>(左外连接)
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
<literal>right outer join</literal>(右外连接)
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
<literal>full join</literal> (全连接,并不常用)
|
||
</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
|
||
<para>
|
||
语句<literal>inner join</literal>, <literal>left outer join</literal> 以及
|
||
<literal>right outer join</literal> 可以简写。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Cat as cat
|
||
join cat.mate as mate
|
||
left join cat.kittens as kitten]]></programlisting>
|
||
|
||
<para>
|
||
还有,一个"fetch"连接允许仅仅使用一个选择语句就将相关联的对象或一组值的集合随着他们的父对象的初始化而被初始化,这种方法在使用到集合的情况下尤其有用,对于关联和集合来说,它有效的代替了映射文件中的外联接
|
||
与延迟声明(lazy declarations). 查看
|
||
<xref linkend="performance-fetching"/> 以获得等多的信息。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Cat as cat
|
||
inner join fetch cat.mate
|
||
left join fetch cat.kittens]]></programlisting>
|
||
|
||
<para>
|
||
一个fetch连接通常不需要被指定别名, 因为相关联的对象不应当被用在
|
||
<literal>where</literal> 子句 (或其它任何子句)中。同时,相关联的对象
|
||
并不在查询的结果中直接返回,但可以通过他们的父对象来访问到他们。
|
||
</para>
|
||
|
||
<para>
|
||
注意<literal>fetch</literal>构造变量在使用了<literal>scroll()</literal> 或 <literal>iterate()</literal>函数
|
||
的查询中是不能使用的。最后注意,使用<literal>full join fetch</literal> 与 <literal>right join fetch</literal>是没有意义的。
|
||
</para>
|
||
|
||
<para>
|
||
如果你使用属性级别的延迟获取(lazy fetching)(这是通过重新编写字节码实现的),可以使用 <literal>fetch
|
||
all properties</literal>
|
||
来强制Hibernate立即取得那些原本需要延迟加载的属性(在第一个查询中)。
|
||
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Document fetch all properties order by name]]></programlisting>
|
||
<programlisting><![CDATA[from Document doc fetch all properties where lower(doc.name) like '%cats%']]></programlisting>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="queryhql-select">
|
||
<title>select子句</title>
|
||
|
||
<para>
|
||
<literal>select</literal> 子句选择将哪些对象与属性返
|
||
回到查询结果集中. 考虑如下情况:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select mate
|
||
from Cat as cat
|
||
inner join cat.mate as mate]]></programlisting>
|
||
|
||
<para>
|
||
该语句将选择<literal>mate</literal>s of other <literal>Cat</literal>s。(其他猫的配偶)
|
||
实际上, 你可以更简洁的用以下的查询语句表达相同的含义:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select cat.mate from Cat cat]]></programlisting>
|
||
|
||
<para>
|
||
查询语句可以返回值为任何类型的属性,包括返回类型为某种组件(Component)的属性:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select cat.name from DomesticCat cat
|
||
where cat.name like 'fri%']]></programlisting>
|
||
|
||
<programlisting><![CDATA[select cust.name.firstName from Customer as cust]]></programlisting>
|
||
|
||
<para>
|
||
查询语句可以返回多个对象和(或)属性,存放在
|
||
<literal>Object[]</literal>队列中,
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select mother, offspr, mate.name
|
||
from DomesticCat as mother
|
||
inner join mother.mate as mate
|
||
left outer join mother.kittens as offspr]]></programlisting>
|
||
|
||
<para>
|
||
或存放在一个<literal>List</literal>对象中,
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select new list(mother, offspr, mate.name)
|
||
from DomesticCat as mother
|
||
inner join mother.mate as mate
|
||
left outer join mother.kittens as offspr]]></programlisting>
|
||
|
||
<para>
|
||
也可能直接返回一个实际的类型安全的Java对象,
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select new Family(mother, mate, offspr)
|
||
from DomesticCat as mother
|
||
join mother.mate as mate
|
||
left join mother.kittens as offspr]]></programlisting>
|
||
|
||
<para>
|
||
假设类<literal>Family</literal>有一个合适的构造函数.
|
||
</para>
|
||
|
||
<para>
|
||
你可以使用关键字<literal>as</literal>给“被选择了的表达式”指派别名:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n
|
||
from Cat cat]]></programlisting>
|
||
|
||
<para>
|
||
这种做法在与子句<literal>select new map</literal>一起使用时最有用:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select new map( max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n )
|
||
from Cat cat]]></programlisting>
|
||
|
||
<para>
|
||
该查询返回了一个<literal>Map</literal>的对象,内容是别名与被选择的值组成的名-值映射。
|
||
</para>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="queryhql-aggregation">
|
||
<title>聚集函数</title>
|
||
|
||
<para>
|
||
HQL查询甚至可以返回作用于属性之上的聚集函数的计算结果:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)
|
||
from Cat cat]]></programlisting>
|
||
|
||
<!-- 已经不再被继续支持
|
||
<para>
|
||
在<literal>select</literal>子句中,集合(Collections)也可以出现在聚集函数的内部。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select cat, count( elements(cat.kittens) )
|
||
from Cat cat group by cat]]></programlisting>
|
||
-->
|
||
|
||
<para>
|
||
受支持的聚集函数如下:
|
||
</para>
|
||
|
||
<itemizedlist spacing="compact">
|
||
<listitem>
|
||
<para>
|
||
<literal>avg(...), sum(...), min(...), max(...)</literal>
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
<literal>count(*)</literal>
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
<literal>count(...), count(distinct ...), count(all...)</literal>
|
||
</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
|
||
<para>
|
||
你可以在选择子句中使用数学操作符、连接以及经过验证的SQL函数:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select cat.weight + sum(kitten.weight)
|
||
from Cat cat
|
||
join cat.kittens kitten
|
||
group by cat.id, cat.weight]]></programlisting>
|
||
|
||
<programlisting><![CDATA[select firstName||' '||initial||' '||upper(lastName) from Person]]></programlisting>
|
||
|
||
<para>
|
||
关键字<literal>distinct</literal>与<literal>all</literal> 也可以使用,它们具有与SQL相同的语义.
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select distinct cat.name from Cat cat
|
||
|
||
select count(distinct cat.name), count(cat) from Cat cat]]></programlisting>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="queryhql-polymorphism">
|
||
<title>多态查询</title>
|
||
|
||
<para>
|
||
一个如下的查询语句:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Cat as cat]]></programlisting>
|
||
|
||
<para>
|
||
不仅返回<literal>Cat</literal>类的实例, 也同时返回子类
|
||
<literal>DomesticCat</literal>的实例. Hibernate 可以在<literal>from</literal>子句中指定<emphasis>任何</emphasis>
|
||
Java 类或接口. 查询会返回继承了该类的所有持久化子类
|
||
的实例或返回声明了该接口的所有持久化类的实例。下面的查询语句返回所有的被持久化的对象:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from java.lang.Object o]]></programlisting>
|
||
|
||
<para>
|
||
接口<literal>Named</literal> 可能被各种各样的持久化类声明:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Named n, Named m where n.name = m.name]]></programlisting>
|
||
|
||
<para>
|
||
注意,最后的两个查询将需要超过一个的SQL <literal>SELECT</literal>.这表明<literal>order by</literal>子句
|
||
没有对整个结果集进行正确的排序.
|
||
(这也说明你不能对这样的查询使用<literal>Query.scroll()</literal>方法.)
|
||
</para>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="queryhql-where">
|
||
<title>where子句</title>
|
||
|
||
<para>
|
||
<literal>where</literal>子句允许你将返回的实例列表的范围缩小.
|
||
如果没有指定别名,你可以使用属性名来直接引用属性:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Cat where name='Fritz']]></programlisting>
|
||
|
||
<para>
|
||
如果指派了别名,需要使用完整的属性名:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Cat as cat where cat.name='Fritz']]></programlisting>
|
||
|
||
<para>
|
||
返回名为(属性name等于)'Fritz'的<literal>Cat</literal>类的实例。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select foo
|
||
from Foo foo, Bar bar
|
||
where foo.startDate = bar.date]]></programlisting>
|
||
|
||
<para>
|
||
将返回所有满足下面条件的<literal>Foo</literal>类的实例:
|
||
存在如下的<literal>bar</literal>的一个实例,其<literal>date</literal>属性等于
|
||
<literal>Foo</literal>的<literal>startDate</literal>属性。
|
||
复合路径表达式使得<literal>where</literal>子句非常的强大,考虑如下情况:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Cat cat where cat.mate.name is not null]]></programlisting>
|
||
|
||
<para>
|
||
该查询将被翻译成为一个含有表连接(内连接)的SQL查询。如果你打算写像这样的查询语句
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Foo foo
|
||
where foo.bar.baz.customer.address.city is not null]]></programlisting>
|
||
|
||
<para>
|
||
在SQL中,你为达此目的将需要进行一个四表连接的查询。
|
||
</para>
|
||
|
||
<para>
|
||
<literal>=</literal>运算符不仅可以被用来比较属性的值,也可以用来比较实例:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Cat cat, Cat rival where cat.mate = rival.mate]]></programlisting>
|
||
|
||
<programlisting><![CDATA[select cat, mate
|
||
from Cat cat, Cat mate
|
||
where cat.mate = mate]]></programlisting>
|
||
|
||
<para>
|
||
特殊属性(小写)<literal>id</literal>可以用来表示一个对象的唯一的标识符。(你也可以使用该对象的属性名。)
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Cat as cat where cat.id = 123
|
||
|
||
from Cat as cat where cat.mate.id = 69]]></programlisting>
|
||
|
||
<para>
|
||
第二个查询是有效的。此时不需要进行表连接!
|
||
</para>
|
||
|
||
<para>
|
||
同样也可以使用复合标识符。比如<literal>Person</literal>类有一个复合标识符,它由<literal>country</literal>属性
|
||
与<literal>medicareNumber</literal>属性组成。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from bank.Person person
|
||
where person.id.country = 'AU'
|
||
and person.id.medicareNumber = 123456]]></programlisting>
|
||
|
||
<programlisting><![CDATA[from bank.Account account
|
||
where account.owner.id.country = 'AU'
|
||
and account.owner.id.medicareNumber = 123456]]></programlisting>
|
||
|
||
<para>
|
||
第二个查询也不需要进行表连接。
|
||
</para>
|
||
|
||
<para>
|
||
同样的,特殊属性<literal>class</literal>在进行多态持久化的情况下被用来存取一个实例的鉴别值(discriminator value)。
|
||
一个嵌入到where子句中的Java类的名字将被转换为该类的鉴别值。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Cat cat where cat.class = DomesticCat]]></programlisting>
|
||
|
||
<para>
|
||
你也可以声明一个属性的类型是组件或者复合用户类型(以及由组件构成的组件等等)。永远不要尝试使用以组件类型来结尾的路径表达式(path-expression)
|
||
(与此相反,你应当使用组件的一个属性来结尾)。
|
||
举例来说,如果<literal>store.owner</literal>含有一个包含了组件的实体<literal>address</literal>
|
||
</para>
|
||
|
||
<programlisting><![CDATA[store.owner.address.city // 正确
|
||
store.owner.address // 错误!]]></programlisting>
|
||
|
||
<para>
|
||
一个“任意”类型有两个特殊的属性<literal>id</literal>和<literal>class</literal>,
|
||
来允许我们按照下面的方式表达一个连接(<literal>AuditLog.item</literal>
|
||
是一个属性,该属性被映射为<literal><any></literal>)。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from AuditLog log, Payment payment
|
||
where log.item.class = 'Payment' and log.item.id = payment.id]]></programlisting>
|
||
|
||
<para>
|
||
注意,在上面的查询与句中,<literal>log.item.class</literal> 和 <literal>payment.class</literal>
|
||
将涉及到完全不同的数据库中的列。
|
||
</para>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="queryhql-expressions">
|
||
<title>表达式</title>
|
||
|
||
<para>
|
||
在<literal>where</literal>子句中允许使用的表达式包括
|
||
大多数你可以在SQL使用的表达式种类:
|
||
</para>
|
||
|
||
<itemizedlist spacing="compact">
|
||
<listitem>
|
||
<para>
|
||
数学运算符<literal>+, -, *, /</literal>
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
二进制比较运算符<literal>=, >=, <=, <>, !=, like</literal>
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
逻辑运算符<literal>and, or, not</literal>
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
<literal>in</literal>,
|
||
<literal>not in</literal>,
|
||
<literal>between</literal>,
|
||
<literal>is null</literal>,
|
||
<literal>is not null</literal>,
|
||
<literal>is empty</literal>,
|
||
<literal>is not empty</literal>,
|
||
<literal>member of</literal> and
|
||
<literal>not member of</literal>
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
"简单的" case, <literal>case ... when ... then ... else ... end</literal>,和
|
||
"搜索" case, <literal>case when ... then ... else ... end</literal>
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
字符串连接符<literal>...||...</literal> or <literal>concat(...,...)</literal>
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
<literal>current_date()</literal>, <literal>current_time()</literal>,
|
||
<literal>current_timestamp()</literal>
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
<literal>second(...)</literal>, <literal>minute(...)</literal>,
|
||
<literal>hour(...)</literal>, <literal>day(...)</literal>,
|
||
<literal>month(...)</literal>, <literal>year(...)</literal>,
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
EJB-QL 3.0定义的任何函数或操作:<literal>substring(), trim(),
|
||
lower(), upper(), length(), locate(), abs(), sqrt(), bit_length()</literal>
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
<literal>coalesce()</literal> 和 <literal>nullif()</literal>
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
<literal>cast(... as ...)</literal>, 其第二个参数是某Hibernate类型的名字,以及<literal>extract(... from ...)</literal>,只要ANSI
|
||
<literal>cast()</literal> 和 <literal>extract()</literal> 被底层数据库支持
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
任何数据库支持的SQL标量函数,比如<literal>sign()</literal>,
|
||
<literal>trunc()</literal>, <literal>rtrim()</literal>, <literal>sin()</literal>
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
JDBC参数传入 <literal>?</literal>
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
命名参数<literal>:name</literal>, <literal>:start_date</literal>, <literal>:x1</literal>
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
SQL 直接常量 <literal>'foo'</literal>, <literal>69</literal>, <literal>'1970-01-01 10:00:01.0'</literal>
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
Java <literal>public static final</literal> 类型的常量 <literal>eg.Color.TABBY</literal>
|
||
</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
|
||
<para>
|
||
关键字<literal>in</literal>与<literal>between</literal>可按如下方法使用:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from DomesticCat cat where cat.name between 'A' and 'B']]></programlisting>
|
||
|
||
<programlisting><![CDATA[from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
|
||
|
||
<para>
|
||
而且否定的格式也可以如下书写:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from DomesticCat cat where cat.name not between 'A' and 'B']]></programlisting>
|
||
|
||
<programlisting><![CDATA[from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )]]></programlisting>
|
||
|
||
<para>
|
||
同样, 子句<literal>is null</literal>与<literal>is not null</literal>可以被用来测试空值(null).
|
||
</para>
|
||
|
||
<para>
|
||
在Hibernate配置文件中声明HQL“查询替代(query substitutions)”之后,
|
||
布尔表达式(Booleans)可以在其他表达式中轻松的使用:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[<property name="hibernate.query.substitutions">true 1, false 0</property>]]></programlisting>
|
||
|
||
<para>
|
||
系统将该HQL转换为SQL语句时,该设置表明将用字符 <literal>1</literal> 和
|
||
<literal>0</literal> 来
|
||
取代关键字<literal>true</literal> 和 <literal>false</literal>:
|
||
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Cat cat where cat.alive = true]]></programlisting>
|
||
|
||
<para>
|
||
你可以用特殊属性<literal>size</literal>, 或是特殊函数<literal>size()</literal>测试一个集合的大小。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Cat cat where cat.kittens.size > 0]]></programlisting>
|
||
|
||
<programlisting><![CDATA[from Cat cat where size(cat.kittens) > 0]]></programlisting>
|
||
|
||
<para>
|
||
对于索引了(有序)的集合,你可以使用<literal>minindex</literal> 与 <literal>maxindex</literal>函数来引用到最小与最大的索引序数。
|
||
同理,你可以使用<literal>minelement</literal> 与 <literal>maxelement</literal>函数来
|
||
引用到一个基本数据类型的集合中最小与最大的元素。
|
||
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Calendar cal where maxelement(cal.holidays) > current date]]></programlisting>
|
||
|
||
<programlisting><![CDATA[from Order order where maxindex(order.items) > 100]]></programlisting>
|
||
|
||
<programlisting><![CDATA[from Order order where minelement(order.items) > 10000]]></programlisting>
|
||
|
||
<para>
|
||
在传递一个集合的索引集或者是元素集(<literal>elements</literal>与<literal>indices</literal> 函数)
|
||
或者传递一个子查询的结果的时候,可以使用SQL函数<literal>any, some, all, exists, in</literal>
|
||
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select mother from Cat as mother, Cat as kit
|
||
where kit in elements(foo.kittens)]]></programlisting>
|
||
|
||
<programlisting><![CDATA[select p from NameList list, Person p
|
||
where p.name = some elements(list.names)]]></programlisting>
|
||
|
||
<programlisting><![CDATA[from Cat cat where exists elements(cat.kittens)]]></programlisting>
|
||
|
||
<programlisting><![CDATA[from Player p where 3 > all elements(p.scores)]]></programlisting>
|
||
|
||
<programlisting><![CDATA[from Show show where 'fizard' in indices(show.acts)]]></programlisting>
|
||
|
||
<para>
|
||
注意,在Hibernate3种,这些结构变量- <literal>size</literal>, <literal>elements</literal>,
|
||
<literal>indices</literal>, <literal>minindex</literal>, <literal>maxindex</literal>,
|
||
<literal>minelement</literal>, <literal>maxelement</literal> - 只能在where子句中使用。
|
||
</para>
|
||
|
||
<para>
|
||
一个被索引过的(有序的)集合的元素(arrays, lists, maps)可以在其他索引中被引用(只能在where子句中):
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Order order where order.items[0].id = 1234]]></programlisting>
|
||
|
||
<programlisting><![CDATA[select person from Person person, Calendar calendar
|
||
where calendar.holidays['national day'] = person.birthDay
|
||
and person.nationality.calendar = calendar]]></programlisting>
|
||
|
||
<programlisting><![CDATA[select item from Item item, Order order
|
||
where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11]]></programlisting>
|
||
|
||
<programlisting><![CDATA[select item from Item item, Order order
|
||
where order.items[ maxindex(order.items) ] = item and order.id = 11]]></programlisting>
|
||
|
||
<para>
|
||
在<literal>[]</literal>中的表达式甚至可以是一个算数表达式。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select item from Item item, Order order
|
||
where order.items[ size(order.items) - 1 ] = item]]></programlisting>
|
||
|
||
<para>
|
||
对于一个一对多的关联(one-to-many association)或是值的集合中的元素,
|
||
HQL也提供内建的<literal>index()</literal>函数,
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select item, index(item) from Order order
|
||
join order.items item
|
||
where index(item) < 5]]></programlisting>
|
||
|
||
<para>
|
||
如果底层数据库支持标量的SQL函数,它们也可以被使用
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from DomesticCat cat where upper(cat.name) like 'FRI%']]></programlisting>
|
||
|
||
<para>
|
||
如果你还不能对所有的这些深信不疑,想想下面的查询。如果使用SQL,语句长度会增长多少,可读性会下降多少:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select cust
|
||
from Product prod,
|
||
Store store
|
||
inner join store.customers cust
|
||
where prod.name = 'widget'
|
||
and store.location.name in ( 'Melbourne', 'Sydney' )
|
||
and prod = all elements(cust.currentOrder.lineItems)]]></programlisting>
|
||
|
||
<para>
|
||
<emphasis>提示:</emphasis> 会像如下的语句
|
||
</para>
|
||
|
||
<programlisting><![CDATA[SELECT cust.name, cust.address, cust.phone, cust.id, cust.current_order
|
||
FROM customers cust,
|
||
stores store,
|
||
locations loc,
|
||
store_customers sc,
|
||
product prod
|
||
WHERE prod.name = 'widget'
|
||
AND store.loc_id = loc.id
|
||
AND loc.name IN ( 'Melbourne', 'Sydney' )
|
||
AND sc.store_id = store.id
|
||
AND sc.cust_id = cust.id
|
||
AND prod.id = ALL(
|
||
SELECT item.prod_id
|
||
FROM line_items item, orders o
|
||
WHERE item.order_id = o.id
|
||
AND cust.current_order = o.id
|
||
)]]></programlisting>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="queryhql-ordering">
|
||
<title>order by子句</title>
|
||
|
||
<para>
|
||
查询返回的列表(list)可以按照一个返回的类或组件(components)中的任何属性(property)进行排序:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from DomesticCat cat
|
||
order by cat.name asc, cat.weight desc, cat.birthdate]]></programlisting>
|
||
|
||
<para>
|
||
可选的<literal>asc</literal>或<literal>desc</literal>关键字指明了按照升序或降序进行排序.
|
||
</para>
|
||
</sect1>
|
||
|
||
<sect1 id="queryhql-grouping">
|
||
<title>group by子句</title>
|
||
|
||
<para>
|
||
一个返回聚集值(aggregate values)的查询可以按照一个返回的类或组件(components)中的任何属性(property)进行分组:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
|
||
from Cat cat
|
||
group by cat.color]]></programlisting>
|
||
|
||
<programlisting><![CDATA[select foo.id, avg(name), max(name)
|
||
from Foo foo join foo.names name
|
||
group by foo.id]]></programlisting>
|
||
|
||
<para>
|
||
<literal>having</literal>子句在这里也允许使用.
|
||
</para>
|
||
|
||
<programlisting><![CDATA[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)]]></programlisting>
|
||
|
||
<para>
|
||
如果底层的数据库支持的话(例如不能在MySQL中使用),SQL的一般函数与聚集函数也可以出现
|
||
在<literal>having</literal>与<literal>order by</literal> 子句中。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[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]]></programlisting>
|
||
|
||
<para>
|
||
注意<literal>group by</literal>子句与
|
||
<literal>order by</literal>子句中都不能包含算术表达式(arithmetic expressions).
|
||
</para>
|
||
|
||
</sect1>
|
||
|
||
|
||
<sect1 id="queryhql-subqueries">
|
||
<title>子查询</title>
|
||
|
||
<para>
|
||
对于支持子查询的数据库,Hibernate支持在查询中使用子查询。一个子查询必须被圆括号包围起来(经常是SQL聚集函数的圆括号)。
|
||
甚至相互关联的子查询(引用到外部查询中的别名的子查询)也是允许的。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Cat as fatcat
|
||
where fatcat.weight > (
|
||
select avg(cat.weight) from DomesticCat cat
|
||
)]]></programlisting>
|
||
|
||
<programlisting><![CDATA[from DomesticCat as cat
|
||
where cat.name = some (
|
||
select name.nickName from Name as name
|
||
)]]></programlisting>
|
||
|
||
<programlisting><![CDATA[from Cat as cat
|
||
where not exists (
|
||
from Cat as mate where mate.mate = cat
|
||
)]]></programlisting>
|
||
|
||
<programlisting><![CDATA[from DomesticCat as cat
|
||
where cat.name not in (
|
||
select name.nickName from Name as name
|
||
)]]></programlisting>
|
||
|
||
<para>
|
||
在select列表中包含一个表达式以上的子查询,你可以使用一个元组构造符(tuple constructors):
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Cat as cat
|
||
where not ( cat.name, cat.color ) in (
|
||
select cat.name, cat.color from DomesticCat cat
|
||
)]]></programlisting>
|
||
|
||
<para>
|
||
注意在某些数据库中(不包括Oracle与HSQL),你也可以在其他语境中使用元组构造符,
|
||
比如查询用户类型的组件与组合:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Person where name = ('Gavin', 'A', 'King')]]></programlisting>
|
||
|
||
<para>
|
||
该查询等价于更复杂的:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from Person where name.first = 'Gavin' and name.initial = 'A' and name.last = 'King')]]></programlisting>
|
||
|
||
<para>
|
||
有两个很好的理由使你不应当作这样的事情:首先,它不完全适用于各个数据库平台;其次,查询现在依赖于映射文件中属性的顺序。
|
||
</para>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="queryhql-examples">
|
||
<title>HQL示例</title>
|
||
|
||
<para>
|
||
Hibernate查询可以非常的强大与复杂。实际上,Hibernate的一个主要卖点就是查询语句的威力。这里有一些例子,它们与我在最近的
|
||
一个项目中使用的查询非常相似。注意你能用到的大多数查询比这些要简单的多!
|
||
</para>
|
||
|
||
<para>
|
||
下面的查询对于某个特定的客户的所有未支付的账单,在给定给最小总价值的情况下,返回订单的id,条目的数量和总价值,
|
||
返回值按照总价值的结果进行排序。为了决定价格,查询使用了当前目录。作为转换结果的SQL查询,使用了<literal>ORDER</literal>,
|
||
<literal>ORDER_LINE</literal>, <literal>PRODUCT</literal>, <literal>CATALOG</literal> 和<literal>PRICE</literal>
|
||
库表。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select order.id, sum(price.amount), count(item)
|
||
from Order as order
|
||
join order.lineItems as item
|
||
join item.product as product,
|
||
Catalog as catalog
|
||
join catalog.prices as price
|
||
where order.paid = false
|
||
and order.customer = :customer
|
||
and price.product = product
|
||
and catalog.effectiveDate < sysdate
|
||
and catalog.effectiveDate >= all (
|
||
select cat.effectiveDate
|
||
from Catalog as cat
|
||
where cat.effectiveDate < sysdate
|
||
)
|
||
group by order
|
||
having sum(price.amount) > :minAmount
|
||
order by sum(price.amount) desc]]></programlisting>
|
||
|
||
<para>
|
||
这简直是一个怪物!实际上,在现实生活中,我并不热衷于子查询,所以我的查询语句看起来更像这个:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select order.id, sum(price.amount), count(item)
|
||
from Order as order
|
||
join order.lineItems as item
|
||
join item.product as product,
|
||
Catalog as catalog
|
||
join catalog.prices as price
|
||
where order.paid = false
|
||
and order.customer = :customer
|
||
and price.product = product
|
||
and catalog = :currentCatalog
|
||
group by order
|
||
having sum(price.amount) > :minAmount
|
||
order by sum(price.amount) desc]]></programlisting>
|
||
|
||
<para>
|
||
下面一个查询计算每一种状态下的支付的数目,除去所有处于<literal>AWAITING_APPROVAL</literal>状态的支付,因为在该状态下
|
||
当前的用户作出了状态的最新改变。该查询被转换成含有两个内连接以及一个相关联的子选择的SQL查询,该查询使用了表
|
||
<literal>PAYMENT</literal>, <literal>PAYMENT_STATUS</literal> 以及
|
||
<literal>PAYMENT_STATUS_CHANGE</literal>。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select count(payment), status.name
|
||
from Payment as payment
|
||
join payment.currentStatus as status
|
||
join payment.statusChanges as statusChange
|
||
where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
|
||
or (
|
||
statusChange.timeStamp = (
|
||
select max(change.timeStamp)
|
||
from PaymentStatusChange change
|
||
where change.payment = payment
|
||
)
|
||
and statusChange.user <> :currentUser
|
||
)
|
||
group by status.name, status.sortOrder
|
||
order by status.sortOrder]]></programlisting>
|
||
|
||
<para>
|
||
如果我把<literal>statusChanges</literal>实例集映射为一个列表(list)而不是一个集合(set),
|
||
书写查询语句将更加简单.
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select count(payment), status.name
|
||
from Payment as payment
|
||
join payment.currentStatus as status
|
||
where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
|
||
or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser
|
||
group by status.name, status.sortOrder
|
||
order by status.sortOrder]]></programlisting>
|
||
|
||
<para>
|
||
下面一个查询使用了MS SQL Server的 <literal>isNull()</literal>函数用以返回当前用户所属组织的组织帐号及组织未支付的账。
|
||
它被转换成一个对表<literal>ACCOUNT</literal>, <literal>PAYMENT</literal>, <literal>PAYMENT_STATUS</literal>,
|
||
<literal>ACCOUNT_TYPE</literal>, <literal>ORGANIZATION</literal> 以及 <literal>ORG_USER</literal>进行的三个内连接,
|
||
一个外连接和一个子选择的SQL查询。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select account, payment
|
||
from Account as account
|
||
left outer join account.payments as payment
|
||
where :currentUser in elements(account.holder.users)
|
||
and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
|
||
order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
|
||
|
||
<para>
|
||
对于一些数据库,我们需要弃用(相关的)子选择。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select account, payment
|
||
from Account as account
|
||
join account.holder.users as user
|
||
left outer join account.payments as payment
|
||
where :currentUser = user
|
||
and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
|
||
order by account.type.sortOrder, account.accountNumber, payment.dueDate]]></programlisting>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="queryhql-bulk">
|
||
<title>批量的UPDATE & DELETE语句</title>
|
||
|
||
<para>
|
||
HQL现在支持UPDATE与DELETE语句. 查阅
|
||
<xref linkend="batch-direct"/> 以获得更多信息。
|
||
</para>
|
||
</sect1>
|
||
|
||
<sect1 id="queryhql-tipstricks">
|
||
<title>小技巧 & 小窍门</title>
|
||
|
||
<para>
|
||
你可以统计查询结果的数目而不必实际的返回他们:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue()]]></programlisting>
|
||
|
||
<para>
|
||
若想根据一个集合的大小来进行排序,可以使用如下的语句:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select usr.id, usr.name
|
||
from User as usr
|
||
left join usr.messages as msg
|
||
group by usr.id, usr.name
|
||
order by count(msg)]]></programlisting>
|
||
|
||
<para>
|
||
如果你的数据库支持子选择,你可以在你的查询的where子句中为选择的大小(selection size)指定一个条件:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[from User usr where size(usr.messages) >= 1]]></programlisting>
|
||
|
||
<para>
|
||
如果你的数据库不支持子选择语句,使用下面的查询:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select usr.id, usr.name
|
||
from User usr.name
|
||
join usr.messages msg
|
||
group by usr.id, usr.name
|
||
having count(msg) >= 1]]></programlisting>
|
||
|
||
<para>
|
||
因为内连接(inner join)的原因,这个解决方案不能返回含有零个信息的<literal>User</literal>
|
||
类的实例, 所以这种情况下使用下面的格式将是有帮助的:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[select usr.id, usr.name
|
||
from User as usr
|
||
left join usr.messages as msg
|
||
group by usr.id, usr.name
|
||
having count(msg) = 0]]></programlisting>
|
||
|
||
<para>
|
||
JavaBean的属性可以被绑定到一个命名查询(named query)的参数上:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[Query q = s.createQuery("from foo Foo as foo where foo.name=:name and foo.size=:size");
|
||
q.setProperties(fooBean); // fooBean包含方法getName()与getSize()
|
||
List foos = q.list();]]></programlisting>
|
||
|
||
<para>
|
||
通过将接口<literal>Query</literal>与一个过滤器(filter)一起使用,集合(Collections)是可以分页的:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[Query q = s.createFilter( collection, "" ); // 一个简单的过滤器
|
||
q.setMaxResults(PAGE_SIZE);
|
||
q.setFirstResult(PAGE_SIZE * pageNumber);
|
||
List page = q.list();]]></programlisting>
|
||
|
||
<para>
|
||
通过使用查询过滤器(query filter)可以将集合(Collection)的原素分组或排序:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[Collection orderedCollection = s.filter( collection, "order by this.amount" );
|
||
Collection counts = s.filter( collection, "select this.type, count(this) group by this.type" );]]></programlisting>
|
||
|
||
<para>
|
||
不用通过初始化,你就可以知道一个集合(Collection)的大小:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[( (Integer) session.iterate("select count(*) from ....").next() ).intValue();]]></programlisting>
|
||
|
||
</sect1>
|
||
|
||
</chapter> |