返回介绍

使用聚合值

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

我们时常需要从数据库中提取摘要信息,尤其是在做报表的时候:有多少?平均值?最长的?HQL 也支持这一功能,办法就是提供与 SQL 类似的聚合函数(aggregate function)。当然,在 HQL 中,这些函数是针对持久化类的属性而进行运算的。

应该怎么做

我们以目前现有的一些查询测试框架为基础试一试吧。首先,在 Track.hbm.xml 中现有的查询内容之后再新添加例 9-15 所示的查询内容。

例 9-15:一个用于收集曲目的聚合信息的查询

<query name="com.oreilly.hh.trackSummary">

<![CDATA[

select count(*),min(track.playTime),max(track.playTime)

from Track as track

]]>

</query>

我原来试图取出平均播放时间,但很不幸,HSQLDB 不知道应该如何为非数值的字段计算平均值,因为这个属性是保存在数据库类型为 date(日期)的字段中。

接下来,我们必须写一个方法去执行这个查询,并显示结果。把例 9-16 所示的代码加进 QueryTest.java,就放在 tracksNoLongerThan()方法的后面。

例 9-16:执行 trackSummary 查询的方法

/**

*Print summary information about all tracks.

*

*@param session the Hibernate session that can retrieve data.

**/

public static void printTrackSummary(Session session){

Query query=session.getNamedQuery("com.oreilly.hh.trackSummary");

Object[]results=(Object[])query.uniqueResult();

System.out.println("Summary information:");

System.out.println("Total tracks:"+results[0]);

System.out.println("Shortest track:"+results[1]);

System.out.println("Longest track:"+results[2]);

}

由于我们只在查询中使用聚合函数,因此我们知道返回的结果只会有一行数据。在这种情况下,就可以使用 Query 接口提供的另一个更加方便的 uniqueResult()方法,这样能免去取回一个列表,并把列表的第一个元素取出来的麻烦。如本节前面的 9.2 节所述,因为我们要求的是几个不同的值,因此结果将会是一个 Object 数组,其元素就是我们请求的那些值,它们的顺序与查询结果中的排列顺序相同。(这段代码看起来有些难看,使用了晦涩难懂的数字式数组引用。这就是我们演示的为什么需要为查询创建“报表对象”的原因了,使用报表对象将会让处理变得比现在要方便得多。在普通的 SQL 语句中,你可以为字段起个名称,通过名称从 ResultSet(结果集)中取回字段值;在 HQL 中,你可以声明类,并在查询中创建这个类的实例。)

我们还需要在 main()中新添加一行代码来调用这个方法。可以将这行代码放在打印输出选择的曲目详细信息的循环之后,如例 9-17 所示。

例 9-17:在 QueryTest.java 中的 main()中新增加一行,以显示新的摘要信息

……

System.out.println("Track:"+aTitle+"[ID="+anID+']');

}printTrackSummary(session);

}finally{

//No matter what, close the session

……

增加好代码以后,执行 ant qtest,就可以得到新的输出,如例 9-18 所示。

例 9-18:摘要输出

……

qtest:

[java]Track:Video Killed the Radio Star[ID=2]

[java]Track:Test Tone 1[ID=7]

[java]Track:Russian Trance[ID=1]

[java]Track:Never Turn Your Back on Mother Earth[ID=12]

[java]Track:Motherless Child[ID=13]

[java]Track:In a Manner of Speaking[ID=9]

[java]Track:Gone[ID=11]

[java]Summary information:

[java]Total tracks:13

[java]Shortest track:00:00:10

[java]Longest track:00:07:39

BUILD SUCCESSFUL

Total time:9 seconds

相当简单吧。我们再试试比较难处理的—从连接表中获取信息。曲目有一个关联到艺人的集合。假设我们想取出和特定艺人相关联的曲目的摘要信息,而不是所有曲目的摘要信息。例 9-19 演示了查询语句中新增加的部分。

例 9-19:获取特定艺人相关的曲目摘要信息

<query name="com.oreilly.hh.trackSummary">

<![CDATA[

select count(*),min(track.playTime),max(track.playTime)

from Track as track

where:artist in elements(track.artists)

]]>

</query>

为了过滤想看到的曲目,我们新增了一个 where 子句,以及一个命名参数 artist。Hibernate 的 in 运算符还有另外一个用途,可以像在普通的 SQL 语句中那样来使用 in 运算符以给定某个属性可能取值的列表,也可以按这里采用的方式来使用 in。这个语句是告诉 Hibernate,我们感兴趣的曲目是其 artists 集合中含有特定艺人的那些曲目。为了调用这一查询,需要对 print-TrackSummary()做点修改,如例 9-20 所示。

例 9-20:改进 printTrackSummary(),以处理特定艺人的曲目

/**

*Print summary information about tracks associated with an artist.

*

*@param artist the artist in whose tracks we're interested.

*@param session the Hibernate session that can retrieve data.

**/

public static void printTrackSummary(Artist artist, Session session){

Query query=session.getNamedQuery("com.oreilly.hh.trackSummary");

query.setParameter("artist",artist);

Object[]results=(Object[])query.uniqueResult();

System.out.println("Summary of tracks by"+artist.getName()+':');

System.out.println("Total tracks:"+results[0]);

System.out.println("Shortest track:"+results[1]);

System.out.println("Longest track:"+results[2]);

}

没多加什么,对吧?最后,调用该方法的那行代码需要用一个参数来指定一个艺人对象。我们可以再次使用 CreateTest.java 中方便的 getArtist()方法。修改 QueryTest.java 的 main()方法中对该方法的调用代码,使其如例 9-21 所示。

例 9-21:调用已改进的 print, TrackSummary()

……

System.out.println("Track:"+title+"[ID="+id+']');

}

printTrackSummary(CreateTest.getArtist("Samuel Barber",

false, session),session);

}finally{

//No matter what, close the session

……

现在,当你执行 ant qtest 时,就会看到类似例 9-22 的信息。

例 9-22:运行摘要查询,以取得艺人 Samuel Barber 的所有曲目

qtest:

[java]Track:Video Killed the Radio Star[ID=2]

[java]Track:Test Tone 1[ID=7]

[java]Track:Russian Trance[ID=1]

[java]Track:Never Turn Your Back on Mother Earth[ID=12]

[java]Track:Motherless Child[ID=13]

[java]Track:In a Manner of Speaking[ID=9]

[java]Track:Gone[ID=11]

[java]Summary of tracks by Samuel Barber:

[java]Total tracks:2

[java]Shortest track:00:06:35

[java]Longest track:00:07:39

发生了什么事

注意:试着以普通的 SQL 做类似的事!

这根本不费吹灰之力,因此值得花点时间来欣赏一下 Hibernate 为我们做了多少事。我们调用的 getArtist()方法会返回与"Samuel Barber"对应的 Artist 实例,再将整个实例对象作为命名参数传递给 HQL 查询,而 Hibernate 也知道应该如何使用 Artist 对象的 id 属性和 TRACK_ARTISTS 表,把连接查询放在一起,以实现我们在例 9-10 中简单描述的那种复杂情况。

得到的结果反映出示例数据中那两个艺人相互混合的"Adagio for Strings"曲目。因为它们都超过了 5 分钟,所以没有出现在曲目列表中。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

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