返回介绍

选择属性和其他部件

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

到目前为止,我们用的查询返回的都是整个持久化对象。这是像 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)这类合成结果时(后面会有演示),这样做就有用处了。)

发布评论

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