返回介绍

编写 HQL 查询

发布于 2025-04-21 21:42:14 字数 8506 浏览 0 评论 0 收藏

我们已经演示过,HQL 查询中用到的语法成分会比 SQL 的少点(比如在第 3 章中,我们使用的查询语句通常就省略了"select"子句)。事实上,惟一真正需要指定的内容就是你感兴趣的类。例 9-1 显示了一个最简单的查询,对于取得数据库中所有持久保存的 Track 实例来说,这完全是一种有效的方式。

注意:HQL 代表 Hibernate Query Language。那 SQL 呢?这得看你问的是谁了。

例 9-1:最简单的 HQL 查询

from Track

没什么内容,对吧?这一 HQL 就相当于例 8-1,在那个例子中我们对 Track 类建立了一个条件查询,但没有提供任何查询条件。

默认情况下,Hibernate 会自动“导入”(import)你映射的每个类的名称,这意味着当你想使用一个类时,不必提供完整的包名称,只需要简单的类名就可以了。只要映射的类名称是惟一的,就不需要在查询中使用完整限定的类名(fully qualified class name)。如果你喜欢,当然也可以这么做,本书就是这样做的,目的是帮助读者记住查询是按照 Java 数据 bean 及其属性来表示的,而并非数据表和字段(在 SQL 查询中使用这些)。例 9-2 的结果和第一个查询的结果完全相同。

例 9-2:显式指定包名,但依然相当简单

from com.oreilly.hh.data.Track

如果需要映射的多个类具有相同的名称,你可以用完全限定的包名来引用每个类,也可以在其映射文件中使用 import 标签为该类或多个类指定替换名称。还可以在映射文件中为最顶层的 hibernate-mapping 标签属性添加一个 auto-import=false 设置,以关掉自动导入(auto-import)的功能。

你可能已经习惯编写查询语句时不区分大小写,因为 SQL 的行为就是如此。大多数时候,HQL 也是如此。但是类和属性的名称显然是例外。和 Java 的其他组成部分一样,它们是区分大小写的,所以你得把大小写弄对。

让我们看一个极端的示例,它把 HQL 的多态(polymorphic)查询能力推向它的逻辑上的极限,以此来了解 HQL 和 SQL 究竟在哪不同。

应该怎么做

突出 SQL 和 HQL 基本差别的最有效方式就是,考虑当以"from java.lang.Object"来进行查询时会发生什么事。乍一看,可能看不出什么意义!事实上,Hibernate 能够支持返回多态结果的查询。如果映射到的类之间是彼此继承的,或者有共同的基类或接口,那么无论是否将这些类映射到相同的数据表或不同的数据表,都可以查询到它们的超类(superclass)。由于每个 Java 对象都继承自 Object,所以这样的查询就等于要求 Hibernate 返回数据库里的每个实体。我们可以对查询测试做个快速的修改,以测试一下这一查询。把 QueryTest.java 复制成 QueryTest3.java,按例 9-3 所示的内容对它进行修改(大多数修改都是将这里不需要的示例查询删除掉,所以例 9-3 中看不到那些被删除的部分)。

如果你正在使用 Eclipse,那么可以直接跳到第 11 章,阅读有关 Hibernate Tools 的章节,再回来继续学习本章的内容。那一章介绍的 HQL Editor 是一个非常方便的工具,可以帮助你处理我们需要查看的各种查询。用它来做实验太有效了,可以接合你自己的修改,帮助你更深入地学习 HQL。

例 9-3:看吧,再也不用 SQL 了

package com.oreilly.hh;

import org.hibernate.*;

import org.hibernate.cfg.Configuration;

import org.hibernate.criterion.*;

import com.oreilly.hh.data.*;

import java.util.*;

/**

*Retrieve all persistent objects

*/

public class QueryTest3{

/**

*Look up and print all entities when invoked from the command line.

*/

public static void main(String args[])throws Exception{

//Create a configuration based on the properties file we've put

//in the standard place.

Configuration config=new Configuration();

config.configure();

//Get the session factory we can use for persistence

SessionFactory sessionFactory=config.buildSessionFactory();

//Ask for a session using the JDBC information we've configured

Session session=sessionFactory.openSession();

try{

//Print every mapped object in the database

List all=session.createQuery("from java.lang.Object").list();❶

for(Object obj:all){

System.out.println(obj);❷

}

}finally{

//No matter what, close the session

session.close();

}

//Clean up after ourselves

sessionFactory.close();

}

}

❶这行简单的代码就调用了奇妙而功能强大的 HQL 查询。

❷接下来所有需要做的就是循环遍历和打印输出我们找到的结果。除了调用取回的对象的 toString()方法以外,我们什么也不能做,因为我们不知道它们是什么类。作为共享接口,Object 没有提供非常深入的方法。

在运行这个示例之前,还得再多做一件事。在 build.xml 中添加另一个构建目标,如例 9-4 所示。

和以前一样,假设你已经按照前面章节的说明搭建好了环境。也可以直接下载使用为这一章提供的示例源代码。如果你从第 7 章过来,将映射文档换成了标注,则要么你需要回到第 6 章的源代码,要么重新下载本章的源代码,因为本章的这些示例需要依靠映射文件的功能。

例 9-4:调用这个特殊查询

<target name="qtest3"description="Retrieve all mapped objects"

depends="compile">

<java classname="com.oreilly.hh.QueryTest3"fork="true">

<classpath refid="project.class.path"/>

</java>

</target>

为了确保创建好所有的样例数据,先要运行 ant schema ctest atest 命令。接着,再执行 ant qtest3 命令以测试新的查询,得到的结果如例 9-5 所示。

例 9-5:Hibernate 能找到一切事物

%ant qtest3

Buildfile:build.xml

prepare:

usertypes:

codegen:

[hibernatetool]Executing Hibernate Tool with a Standard Configuration

[hibernatetool]1.task:hbm2java(Generates a set of.java files)

compile:

[javac]Compiling 4 source files to/Users/jim/Documents/Work/OReilly

/svn_hibernate/current/examples/ch09/classes

qtest3:

[java]com.oreilly.hh.data.Artist@8a137c[name='PPK'actualArtist='null']

[java]com.oreilly.hh.data.Artist@79e4c[name='The Buggles'actualArtist='n

ull']

[java]com.oreilly.hh.data.Artist@29ae5e[name='Laurie Anderson'actualArti

st='null']

[java]com.oreilly.hh.data.Artist@76a9b6[name='William Orbit'actualArtist

='null']

[java]com.oreilly.hh.data.Artist@801919[name='Ferry Corsten'actualArtist

='null']

[java]com.oreilly.hh.data.Artist@efe4ac[name='Samuel Barber'actualArtist

='null']

[java]com.oreilly.hh.data.Artist@8e13ab[name='ATB'actualArtist='null']

[java]com.oreilly.hh.data.Artist@ad44f6[name='Pulp Victim'actualArtist

='null']

[java]com.oreilly.hh.data.Artist@8aaed5[name='Martin L.Gore'actualArtist

='null']

[java]com.oreilly.hh.data.Album@a1644b[title='Counterfeit e.p.'tracks='[

com.oreilly.hh.data.AlbumTrack@132f26[track='com.oreilly.hh.data.Track@d8f246[

title='Compulsion'volume='Volume[left=100,right=100]'sourceMedia='CD']'],c

om.oreilly.hh.data.AlbumTrack@5ae101[track='com.oreilly.hh.data.Track@9eab7[ti

tle='In a Manner of Speaking'volume='Volume[left=100,right=100]'sourceMedia='

CD']'],com.oreilly.hh.data.AlbumTrack@6a16ae[track='com.oreilly.hh.data.Trac

k@10cf62[title='Smile in the Crowd'volume='Volume[left=100,right=100]'source

Media='CD']'],com.oreilly.hh.data.AlbumTrack@f7309a[track='com.oreilly.hh.da

ta.Track@9f332b[title='Gone'volume='Volume[left=100,right=100]'sourceMedia='

CD']'],com.oreilly.hh.data.AlbumTrack@97cf78[track='com.oreilly.hh.data.Trac

k@d86cae[title='Never Turn Your Back on Mother Earth'volume='Volume[left=100,

right=100]'sourceMedia='CD']'],com.oreilly.hh.data.AlbumTrack@b5d53a[track=

'com.oreilly.hh.data.Track@c74b55[title='Motherless Child'volume='Volume[left=

100,right=100]'sourceMedia='CD']']]']

[java]com.oreilly.hh.data.Track@e74d83[title='Russian Trance'volume='Vol

ume[left=100,right=100]'sourceMedia='CD']

[java]com.oreilly.hh.data.Track@5d8362[title='Video Killed the Radio Star

'volume='Volume[left=100,right=100]'sourceMedia='VHS']

[java]com.oreilly.hh.data.Track@5c9ab5[title='Gravity's Angel'volume='Vo

lume[left=100,right=100]'sourceMedia='CD']

[java]com.oreilly.hh.data.Track@b0e76a[title='Adagio for Strings(Ferry C

orsten Remix)'volume='Volume[left=100,right=100]'sourceMedia='CD']

[java]com.oreilly.hh.data.Track@28ea3f[title='Adagio for Strings(ATB Rem

ix)'volume='Volume[left=100,right=100]'sourceMedia='CD']

[java]com.oreilly.hh.data.Track@2af08b[title='The World'99'volume='Volu

me[left=100,right=100]'sourceMedia='STREAM']

[java]com.oreilly.hh.data.Track@164feb[title='Test Tone 1'volume='Volume

[left=50,right=75]'sourceMedia='null']

[java]com.oreilly.hh.data.Track@d8f246[title='Compulsion'volume='Volume[

left=100,right=100]'sourceMedia='CD']

[java]com.oreilly.hh.data.Track@9eab7[title='In a Manner of Speaking'vol

ume='Volume[left=100,right=100]'sourceMedia='CD']

[java]com.oreilly.hh.data.Track@10cf62[title='Smile in the Crowd'volume=

'Volume[left=100,right=100]'sourceMedia='CD']

[java]com.oreilly.hh.data.Track@9f332b[title='Gone'volume='Volume[left=1

00,right=100]'sourceMedia='CD']

[java]com.oreilly.hh.data.Track@d86cae[title='Never Turn Your Back on Mot

her Earth'volume='Volume[left=100,right=100]'sourceMedia='CD']

[java]com.oreilly.hh.data.Track@c74b55[title='Motherless Child'volume='V

olume[left=100,right=100]'sourceMedia='CD']

BUILD SUCCESSFUL

Total time:9 seconds

发生了什么事

嗯,仔细想一想,这可真不简单,因为 Hibernate 得分别做好几次 SQL 查询才能获得我们想要的所有结果。Hibernate 在幕后做了很多工作,才能把每个它知道的持久化实体的列表交给我们。虽然很难想象实际需要这么做的情况,但这确实突出了 HQL 和 Hibernate 有趣的能力所在。

有时候某些查询确实有些难以理解,但却非常有用。所以记住这一点很有价值:跨表的多态查询(polymorphic query)不仅可行,而且容易使用。

当你执行需要多条 SQL 查询才能完成的 HQL 查询时,会有些限制需要注意。不能使用 order by 子句对整个结果集进行排序,也不能使用 Query 接口 scroll()方法遍历整个结果集。

其他

关联(association)和连接(join)呢?这些也容易处理。只要使用点号作为分隔符(delimiter),顺着属性链走,就能访问关联对象。为了帮助你引用查询表达式中的特定实体,HQL 可以让你指定别名(就像 SQL 一样)。如果你想引用同一个类的两个不同的实体,别名就特别重要,例如:

from com.oreilly.hh.Track as track1

相当于:

from com.oreilly.hh.Track track1

采用哪一种方法取决于你的习惯,或者取决于你为项目准备的编码风格指南。

我们对其他 HQL 元素进行了足够的介绍,就是想让它们吸引起你的兴趣。稍后我们会看看一些连接的示例。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。