返回介绍

检索持久化对象

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

现在,是该来个 180 度大转弯,看看如何从数据库加载数据到 Java 对象中。

使用 Hibernate 查询语言(Hibernate Query Language, HQL),就能以面向对象的方法来检索映射到数据库表的内容。这些数据可以是以前的会话中保存的持久化对象,也可以是完全来自于应用程序代码以外的数据。

应该怎么做

例 3-7 演示了一个程序,对我们刚才创建的测试数据进行简单的查询。整体的结构看起来应该非常熟悉,因为所有的 Hibernate 设置都和上一个程序相同。

例 3-7:数据检索测试 QueryTest.java

package com.oreilly.hh;

import org.hibernate.*;

import org.hibernate.cfg.Configuration;

import com.oreilly.hh.data.*;

import java.sql.Time;

import java.util.*;

/**

*Retrieve data as objects

*/

public class QueryTest{

/**

*Retrieve any tracks that fit in the specified amount of time.

*

*@param length the maximum playing time for tracks to be returned.

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

*@return a list of{@link Track}s meeting the length restriction.

*/

public static List tracksNoLongerThan(Time length, Session session){❶

Query query=session.createQuery("from Track as track"+

"where track.playTime<=?");

query.setParameter(0,length, Hibernate.TIME);

return query.list();

}

/**

*Look up and print some tracks 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 the tracks that will fit in five minutes

List tracks=tracksNoLongerThan(Time.valueOf("00:05:00"),

session);

for(ListIterator iter=tracks.listIterator();

iter.hasNext();){

Track aTrack=(Track)iter.next();

System.out.println("Track:\""+aTrack.getTitle()+

"\","+aTrack.getPlayTime());

}

}finally{

//No matter what, close the session

session.close();

}

//Clean up after ourselves

sessionFactory.close();

}

}

同样,如例 3-8 所示,在 build.xml 的末尾(在 project 的关闭标签以前)添加一个构建目标,以运行这个测试。

例 3-8:调用查询测试的 Ant 构建目标

<target name="qtest"description="Run a simple Hibernate query"

depends="compile">

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

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

</java>

</target>

准备好以后,只要输入 ant qtest,就可以检索出数据并显示出来,其结果如例 3-9 所示。为了节省输出结果占据的空间,我们编辑 log4j.properties 文件,将所有"info"级别的信息输出都关掉,因为这些信息和前一个例子没有差别。你可以修改一下这一行:

log4j.logger.org.hibernate=info

将 info 替换成 warn:

log4j.logger.org.hibernate=warn

例 3-9:运行查询测试

%ant qtest

Buildfile:build.xml

prepare:

[copy]Copying 1 file to/Users/jim/svn/oreilly/hib_dev_2e/current

/examples/ch03/classes

compile:

[javac]Compiling 1 source file to/Users/jim/svn/oreilly/hib_dev_2e

/current/examples/ch03/classes

qtest:

[java]Hibernate:select track0_.TRACK_ID as TRACK1_0_,track0_.title as ti

tle0_,track0_.filePath as filePath0_,track0_.playTime as playTime0_,track0_.a

dded as added0_,track0_.volume as volume0_from TRACK track0_where track0_.pla

yTime<=?

[java]Track:"Russian Trance",00:03:30

[java]Track:"Video Killed the Radio Star",00:03:49

BUILD SUCCESSFUL

Total time:2 seconds

发生了什么事

❶我们先是定义了一个工具方法:tracksNoLongerThan(),由它执行真正的 Hibernate 查询,取回播放时间小于或等于参数指定的值的任何曲目。注意 HQL(Hibernate 根据 SQL 独创的查询语言)支持参数占位符,非常像 JDBC 中的 PreparedStatement。而且,与之类似的是,使用参数占位符更适合于通过字符串处理将所有查询集中在一起(尤其是这样可以免受 SQL 注入的攻击)。不过,你将看到,Hibernate 提供了在 Java 中更好的使用查询的方法。

查询本身看起来有些古怪。它以 from 作为开始,而不是你可能期待的像 select something 之类的语句。虽然你确实可以使用与标准 SQL 更加类似的格式,而且当需要在查询中从一个对象提取出单独的属性时,也只能这么做;如果你想获取整个对象,就可以使用这种更简洁的语法。

还要注意的是,查询是按照映射的 Java 对象和属性来表达的,而不是数据表和列。在这个例子中这一区别还不明显,因为对象和数据表的名称都相同,属性和列的名称也都相同,但查询确实是按照对象和属性来表达的。保持二者的名称一致是一种相当自然的选择,如果使用 Hibernate 来生成数据库模式和数据对象,结果也总是这样,除非你明确地告诉 Hibernate 使用不同的列名称。

当你使用的是以前就有的数据库和对象时,就应该特别留意 HQL 查询引用的是对象属性,而不是数据库表的列。

此外,就像在 SQL 中一样,可以为数据库表和列起其他的别名。在 HQL 中,也可以为类起别名,以方便选择它们的属性或增加约束条件。当然,在这个简单的例子中不会看到这种用法,但是如果你深入研究附录 E 中的内容,肯定会遇到这种用法。

❷这个程序的其他部分看起来可能和上一个例子类似。这里我们简化了 try 块的处理,因为没有改变任何数据,所以就不需要显式地访问事务。我们仍旧使用 try 块,这样就能够有一个 finally 子句,以清晰地关闭会话。代码体本身很简单,调用我们的查询方法以请求播放时间小于或等于 5 分钟的所有曲目,接着再遍历结果中的 Track 对象,分别打印它们的标题和播放时间。

既然我们已经关掉了 Hibernate 内部"info"级别的日志输出,那么我们在 hibernate.cfg.xml 中配置的 SQL 调试输出就更容易定位了。输出中"qtest:"部分的第 1 行并不是我们自己在 QueryTest.java 中编写的,我们看到的 SQL 语句是 Hibernate 生成的,以实现我们请求的 HQL 查询。这些内幕信息很有趣吧!如果你对这些信息也不感兴趣,则可以将 show_sql 属性设置为 false,就不会看到它们了。

其他

如果你已经对数据创建脚本生成的数据进行了修改,现在又想清空数据库,以一种“干净的状态”(clean slate)来进行测试,那么你需要做的就是再次运行 ant schema 命令。这样会将原有的 TRACK 表完全删除,并重新创建新的 TRACK 表,使其处于最原始的空状态。除非你真的需要这样,否则不要轻易这样做!

如果你想选择性地删除一些数据,则或者通过 HSQLDB UI(ant db)里的 SQL 命令;或者先进行一定的查询,检索回你想删除的对象。在取得持久化对象的引用以后,可以将该对象的引用传递给 Session 的 delete()方法,这样就可以将它从数据库中删除了:

session.delete(aTrack);

直到 aTrack 变量超出其作用域范围(scope)或者重新赋值以前,你的程序还至少有一个指向这个被删除对象的引用。因此,就概念上而言,理解 delete()方法最简单的方式就是将其视为把一个持久化对象(persistent object)转变为一个瞬时对象(transient object)。

删除对象的另外一种方法就编写一条可以匹配多个对象的 HQL 删除查询语句。这样可以一次性删除多个持久化对象,而不管这些对象是否在内存中,还不用自己写循环。除了使用 ant schema 命令,改用 Java 方法,以较“温和”的手法来清除掉所有曲目时,就可以像这样写:

Query query=session.createQuery("delete from Track");

query.executeUpdate();

不要忘了,无论采用哪种方法,都得将数据处理的代码放在 Hibernate 事务处理以内,如果想让修改的内容“固定”下来,就得提交(commit)该事务。

发布评论

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