- 译者序
- 前言
- 本书怎么使用
- 本书排版字体约定
- 本书网站
- 致谢
- 第一部分 Hibernate 快速入门
- 第 1 章 安装和设置
- 第 2 章 映射简介
- 第 3 章 驾驭 Hibernate
- 第 4 章 集合与关联
- 第 5 章 更复杂的关联
- 第 6 章 自定义值类型
- 第 7 章 映射标注
- 第 8 章 条件查询
- 第 9 章 浅谈 HQL
- 第二部分 与其他工具的集成
- 第 10 章 将 Hibernate 连接到 MySQL
- 第 11 章 Hibernate 与 Eclipse:Hibernate Tools 使用实战
- 第 12 章 Maven 进阶
- 第 13 章 Spring 入门:Hibernate 与 Spring
- 第 14 章 画龙点睛:用 Stripes 集成 Spring 和 Hibernate
- 附录 A Hibernate 类型
- 附录 B Criteria API
- 附录 C Hibernate SQL 方言
- 附录 D Spring 事务支持
- 附录 E 参考资源
- 作者简介
- 封面介绍
选择属性和其他部件
到目前为止,我们用的查询返回的都是整个持久化对象。这是像 Hibernate 这类对象/关系映射服务组件最常见的用法,所以应该没什么可奇怪的。在得到这些持久化对象以后,就可以在你熟悉的 Java 代码世界中用它们做任何你想做的事。但是在有些情况下,你可能只想取出组成对象的所有属性的一个子集,例如生成报表。HQL 也能满足这样的需要,和普通 SQL 的使用方式一样,也就是在 select 子句中使用 SQL 投影(SQL-projection)。
应该怎么做
假设我们想在 QueryTest.java 的基础上进行修改,使其只显示符合查询条件的曲目的标题,而且想从数据库中一开始提取的信息就只包含曲目的标题。首先,要修改例 3-11 的查询,让它只检索回 title 属性。编辑 Track.hbm.xml,使其查询内容看起来如例 9-6 所示。
例 9-6:只获取短曲目的标题
<query name="com.oreilly.hh.tracksNoLongerThan">
<![CDATA[
select track.title from com.oreilly.hh.Track as track
where track.playTime<=:length
]]>
</query>
要确保让 QueryTest.java 中的 tracksNoLongerThan()方法使用这个查询。(如果你在第 8 章将它修改成使用条件查询,就要再改回例 3-12 的样子。为了节省查找的麻烦,例 9-7 又重新生成了一次。)
例 9-7:HQL 驱动的查询方法,使用例 9-6 中映射的查询
public static List tracksNoLongerThan(Time length, Session session){
Query query=session.getNamedQuery(
"com.oreilly.hh.tracksNoLongerThan");
query.setTime("length",length);
return query.list();
}
最后,还需要更新 main()方法(如例 9-8 所示),以反映一个事实:这个查询方法现在返回的是 title 属性,而不是整个 Track 记录。这个属性的类型是 String,所以,现在这个查询方法返回的是一个由 String 组成的 List 对象。
例 9-8:修改 QueryTest 的 main()方法,以处理标题查询
//Print the titles of tracks that will fit in five minutes
List titles=tracksNoLongerThan(Time.valueOf("00:05:00"),
session);
for(ListIterator iter=titles.listIterator();
iter.hasNext();){
String title=(String)iter.next();
System.out.println("Track:"+title);
}
这些修改都相当简单,而 Java 程序中查询返回的类型和 List 元素之间的关系也一目了然。使用 ant qtest 命令运行这个例子,就会得到类似例 9-9 的输出结果,至于实际显示的数据,则取决于你已经设定的数据。(如果还没有任何数据,或者你想要的结果,输出前可以执行 ant schema ctest atest qtest,这样会重建测试数据。)
例 9-9:只列出时间长度不超过 5 分钟的曲目的标题
qtest:
[java]Track:Russian Trance
[java]Track:Video Killed the Radio Star
[java]Track:Test Tone 1
[java]Track:In a Manner of Speaking
[java]Track:Gone
[java]Track:Never Turn Your Back on Mother Earth
[java]Track:Motherless Child
其他
要返回多个属性?当然可以。如果你使用连接(join)或者你的查询对象中有多个组件或关联(毕竟这是面向对象关联非常方便的方式),在这些情况下,属性都可以来自多个对象。如同 SQL 那样,你所做的就是列出需要的属性,并以逗号分隔。举一个简单的例子,假设我们要得到曲目的标题和 ID。调整 Track.hbm.xml,使查询内容如例 9-10 所示。
例 9-10:从一个对象中选择多个属性
<query name="com.oreilly.hh.tracksNoLongerThan">
<![CDATA[
select track.id, track.title from com.oreilly.hh.Track as track
where track.playTime<=:length
]]>
</query>
根本不需要修改查询方法,还是以相同的名称调用这个查询,并传递相同的命名参数,再返回保存有结果的列表。但是这个列表中现在包含什么?我们需要更新 main()中的循环,才能同时显示曲目的 ID 和标题。
像这种情况,查询结果的每一“行”(row)都要返回多个值,所以 Hibernate 返回的 List 对象中的每个条目都会包含一个对象数组。每个数组都包含我们所选择的属性,排列顺序按照各属性在查询语句中的位置而定。这样,我们会得到一个包含两个元素的数组的列表,每个数组内包含一个 Integer 对象,再接着包含一个 String 对象。
例 9-11 演示了如何更新 QueryTest.java 中的 main(),以处理这些数组。
例 9-11:处理查询结果中多个单独的属性
//Print IDs and titles of tracks that will fit in five minutes
List titles=tracksNoLongerThan(Time.valueOf("00:05:00"),
session);
for(ListIterator iter=titles.listIterator();
iter.hasNext();){
Object[]row=(Object[])iter.next();
Integer id=(Integer)row[0];
String title=(String)row[1];
System.out.println("Track:"+aTitle+"[ID="+id+']');
}
经过这些修改以后,执行 ant qtest,可以得到类似例 9-12 的输出。
例 9-12:列出曲目的标题和 ID
qtest:
[java]Track:Russian Trance[ID=1]
[java]Track:Video Killed the Radio Star[ID=2]
[java]Track:Test Tone 1[ID=7]
[java]Track:In a Manner of Speaking[ID=9]
[java]Track:Gone[ID=11]
[java]Track:Never Turn Your Back on Mother Earth[ID=12]
[java]Track:Motherless Child[ID=13]
我希望你在看这个示例时会想:“这种使用 Track 对象属性的方法真差!”。如果你没这么想,比较一下例 9-11 和例 3-7 中的对应代码。后者更为精简而自然,甚至能够显示出更多的曲目信息。如果要提取出映射对象的信息,最好是充分利用映射的能力以提取出对象的真正的实例,这样你就能用表达能力更强而且类型安全的 Java 代码来处理它的属性。
注意:这是那种残酷的玩笑吗?
既然如此,为什么还要这样做?嗯,在某些情况下,用 HQL 检索回多个值有其用途所在。例如,对某些映射类,你只想从每个类中取出一个属性;或者,你可能想通过在 select 子句中列出一些类名来返回一组相关的类。对于这些情况而言,知道这种技巧有其价值所在。如果你的映射对象有很多大型的(或非延迟加载关联的)属性,但你只对其中的一两个属性感兴趣,这一技巧也许能在性能上让你受益匪浅。
此外,当你从不同映射对象中选择一大组属性以建立报表时,还有另一种技巧可以用来方便地构建良好的对象结构。HQL 可以让你在 select 子句内构建并返回任意对象。所以,你可以创建一个专门的报表类,它的属性就是你的报表需要的值,然后在查询中返回这个报表类的实例,而不是返回晦涩难懂的 Object 数组。如果我们定义一个具有 id 和 title 属性的 TrackSummary 类,以及与之相配的构造函数,如此一来就能够使用以下的查询:
select new TrackSummary(track.id, track.title)
而不是使用:
select track.id, track.title
这样,我们就不用在处理查询结果的代码中对数组进行处理。(再一次,就这个例子来说,简单地返回整个 Track 实例是比较有意义的,但是当你要使用来自多个对象的属性或者像聚合函数(aggregate function)这类合成结果时(后面会有演示),这样做就有用处了。)
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论