- 译者序
- 前言
- 本书怎么使用
- 本书排版字体约定
- 本书网站
- 致谢
- 第一部分 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 参考资源
- 作者简介
- 封面介绍
检索持久化对象
现在,是该来个 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)该事务。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论