第 14 章 用 hibernate 检索数据

Post on 16-Mar-2016

99 Views

Category:

Documents

6 Downloads

Preview:

Click to see full reader

DESCRIPTION

第 14 章 用 Hibernate 检索数据. - PowerPoint PPT Presentation

TRANSCRIPT

第 14 章 用 Hibernate 检索数据• 如果不知道所要寻找的对象的持久化标识,那么就无法通过 load() 或 get() 函数来获取持久化对象,这里就需要使用查询。 Hibernate 支持强大且易于使用的面向对象查询语言( HQL )。如果希望通过编程的方式创建查询, Hibernate 提供了完善的按条件( Query By Criteria , QBC )以及按样例( Query By Example , QBE )进行查询的功能。也可以用本地 SQL ( native SQL )描述查询, Hibernate 额外提供了将结果集( result set )转化为对象的支持。本章将详细介绍在 Hibernate 中如何查询数据

14.1 查询基础• 序言中提到的几种查询方式虽然方法各有不同,但基本的操作顺序都是一样的,都需要准备查询条件、执行查询操作、处理查询结果。不同的方法需要选择不同的工具类,在个别情况下也可以混用。

14.1.1 执行查询• HQL 和本地 SQL(native SQL) 查询要通过为 org.hibernate.Query 的实例来表达。这个接口提供了参数绑定、结果集处理以及运行实际查询的方法。总是可以通过当前 Session 获取一个 Query对象,如代码 14-1 所示,列出了几种使用 Quey来执行查询的例子:• 这里提前介绍一下什么是 HQL ? HQL 是 HibernateQuery Language 的缩写, HQL 的语法很像SQL 的语法,但 HQL 是一种面向对象的查询语言。因此, SQL 的操作对象是数据表和列等数据对象,而 HQL 的操作对象是类、实例、属性等。下一节将详细介绍它。

14.1.2 用 iterate() 方法迭代查询结果• 在某些情况下,可以使用 iterate() 方法得到更好的性能。这通常是预期返回的结果在 session ,或二级缓存 (second-level cache) 中已经存在时的情况。如若不然, iterate() 会比 list() 慢,而且可能简单查询也需要进行多次数据库访问: iterate() 会首先使用 1 条语句得到所有对象的持久化标识 (identifiers) ,再根据持久化标识执行多条附加的 select 语句实例化实际的对象。

14.1.3 指定绑定参数• 除了 JDBC 风格的问号 (?) 接收参数外,接口 Query 还提供了一种对命名参数 (named parameters) 参数进行绑定的方法。命名参数 (named parameters) 在查询字符串中是形如: name 的标识符。如果按 JDBC 绑定参数, Hibernate 对参数从 0 开始计数。• 如代码 14-3 所示,给出了按顺序绑定参数和按名称绑定参数的例子。两者方法相同都是 setString() 或 setInterger() ,但方法的第一个参数不同,一个是整数表示顺序,另一个是 String 类型表示名称。

14.1.4 用 Hibernate 简单分页• 如果需要指定结果集的范围(希望返回的最大行数 / 或开始的行数),应该使用 Query 接口提供的方法:• Query q = sess.createQuery("from DomesticPerson person");• q.setFirstResult(20);• q.setMaxResults(10);• List persons = q.list();

14.1.5 可滚动遍历 (Scrollable iteration)• 如果当前使用 JDBC 驱动支持可滚动的结果集( JDBC2.0 后一般的主流数据库驱动都支持可滚动结果集), Query 接口还可以使用 ScrollableResults ,允许用户在查询结果中上下移动游标, • 注意:使用此功能需要保持数据库连接(以及游标 (cursor) )处于一直打开状态。如果需要断开连接使用分页功能,请使用 setMaxResult()/setFirstResult() 。

14.2 HQL 语句详解• HQL 的概念在本章的一开始就做过介绍,它是 Hibernate 专用的查询语言,语法与 SQL 类似,但操作的目标是对象。 HQL 是完全面向对象的查询语言,因此可以支持继承和多态等特征。

14.2.1 HQL 基础• HQL 查询依赖于 Query 类,每个 Query 实例对应一个查询对象。使用 HQL 查询可按如下步骤进行:• ( 1 )获取 HibernateSession 对象;• ( 2 )编写 HQL 语句;• ( 3 )以 HQL 语句作为参数,调用 Session 的 createQuery 方法创建查询对象;• ( 4 )如果 HQL 语句包含参数,调用 Query 的setXxx 方法为参数赋值;• ( 5 )调用 Query 对象的 list 等方法遍历查询结果。

14.2.2 用 from 子句指定数据表• from 子句是最简单的 HQL 语句,也是最基本的HQL 语句。 from 关键字后紧跟持久化类的类名。例如:• from Person• 表明从 Person 持久化类中选出全部的实例。大部分时候,推荐为该 Person 的每个实例起别名。例如:• from Person as p

14.2.3 select 子句查询数据• select 子句虽然不是必须的(在 SQL 中 select 是必须的),但其作用非常重要,主要有以下几种用法:• ( 1 )查询单个属性。 select 子句用于确定选择出的属性,当然 select 选择的属性必须是 from 后持久化类包含的属性。例如: select p.name from Person as p• ( 2 )查询组件中的属性。 select 可以选择任意属性,不仅可以选择持久化类的直接属性,还可以选择组件属性包含的属性,例如: select p.name.firstName from Person as p• ( 3 )查询多个属性。查询语句可以返回多个对象和(或)属性,存放在 Object[] 队列中,例如: select p.name , p.address from Person as p

14.2.3 select 子句查询数据• ( 4 )把多个属性封装成一个 list 。 select 也支持将选择出的属性存入一个 List 对象中,例如: select new list(p.name , p.address) from Person as p• ( 5 )封装成对象。甚至可以将选择出的属性直接封装成对象,例如: select new ClassTest(p.name , p.address) from Person as p• 前提是 ClassTest 支持 p.name 和 p.address 的构造器,假如 p.name 的数据类型是 String , p.address 的数据类型是 String ,则 ClassTest 必须有如下的构造器: ClassTest(String s1 , String s2)• ( 6 )封装为 map 。 select 还支持给选中的表达式命名别名,例如: select p.name as personName from Person as p

14.2.4 HQL 中的聚集函数• HQL 也支持在选出的属性上,使用聚集函数。 HQL 支持的聚集函数与 SQL 完全相同,有如下 5 个:• avg ,计算属性平均值。• count ,统计选择对象的数量。• max ,统计属性值的最大值• min ,统计属性值的最小值。• sum ,计算属性值的总和。

14.2.5 多态查询• 关于多态已经在“复杂映射关系”一章中“继承关系映射”有过详细介绍。 HQL 查询语句被设计成能理解多态查询, from 后跟的持久化类名,不仅会查询出该持久化类的全部实例,还会查询出该类的子类的全部实例。如下面的查询语句:• from Person as p• 该查询语句不仅会查询出 Person 的全部实例,还会查询出 Person 的子类,如 Manger 、 Employee 、 Customer 的全部实例,前提是 Person和 Manger 、 Employee 、 Customer 这些子类完成了正确的继承映射。

14.2.6 用 where 子句添加查询条件• where 子句用于筛选选中的结果,缩小选择的范围。如果没有为持久化实例命名别名,可以直接使用属性名引用属性。如下面的 HQL 查询语句:• from Person where name like 'tom%'• 如果为持久化实例命名了别名,则应该使用完整的属性名。• from Person as p where p.name like "tom%"• 复合属性表达式加强了 where 子句的功能,例如如下 HQL 查询语句:• from Person p where p.address.country like "us%"

14.2.7 查询条件中的表达式• HQL 的功能非常丰富, where 子句后支持的运算符异常丰富,不仅包括 SQL 的运算符,还包括EJB-QL 的运算符等。 where 子句中允许使用大部分 SQL 支持的表达式:• 数学运算符 + 、–、 * 、 / 等。• 二进制比较运算符 = 、 >= 、 <= 、 <> 、 != 、 like 等。• 逻辑运算符 and 、 or 、 not 等。• in 、 not in 、 between 、 is null 、 is not null 、is empty 、 is not empty 、 member of 和 not member of 等。

14.2.8 用 order by 子句排序• 查询返回的列表 (list) 可以根据类或组件属性的任何属性进行排序,例如:• from Person as p• order by p.name , p.age• 还可使用 asc 或 desc 关键字指定升序或降序的排序规则,例如:• from Person as p• order by p.name asc , p.age desc• 如果没有指定排序规则,默认采用升序规则。即是否使用 asc 关键字是没有区别的,加 asc 是升序排序,不加 asc 也是升序排序。

14.2.9 用 group by 子句分组• 返回聚集值的查询可以对持久化类或组件属性的属性进行分组,分组所使用的 group by 子句。看下面的 HQL 查询语句:• select person.age , sum(person.weight) , count(person)• from Person person• group by person. age

14.2.10 HQL 的子查询• 如果底层数据库支持子查询,则可以在 HQL 语句中使用子查询。与 SQL 中子查询相似的是, HQL 中的子查询也需要使用 () 括起来。如:• from Person as fatPerson• where fatPerson.weight >( select avg(person.weight) from Manager person )• 如果 select 中包含多个属性,则应该使用元组构造符:• from Person as p• where not ( p.name , p.age ) in (• select m.name , m.age from Manger m• )

14.2.11 用 fetch 关键字来延时加载集合• 对于集合属性, Hibernate 默认采用延迟加载策略。例如,对于持久化类 Person ,有集合属性 scores 。加载 Person 实例时,默认不加载 scores 属性。如果 Session 被关闭, Person 实例将无法访问关联的 scores 属性。• 为了解决该问题,可以在 Hibernate 映射文件中取消延迟加载或使用 fetch join ,例如:• from Person as p join p.scores

14.2.12 HQL 语句放入配置文件中• HQL 查询还支持将查询所用的 HQL 语句放入配置文件中,而不是代码中。通过这种方式,可以大大提供程序的解耦。使用 query 元素定义命名查询,在 mapping 文件中与 class 元素是并列关系。

14.3 条件查询• HQL 极为强大,但是有些人希望能够动态的使用一种面向对象 API 创建查询,而非在他们的 Java代码中嵌入字符串。对于那部分人来说, Hibernate 提供了直观的 Criteria 查询 API , Query By Criteria 简称 QBC 。

14.3.1 添加查询条件• 一个单独的查询条件是 org.hibernate.criterion.Criterion接口的一个实例。 Hibernate 提供了一个工具类 --org.hibernate.criterion.Restrictions ,它定义了获得某些内置 Criterion 类型的工厂方法,来构造 Restrictions 对象,如:• // 添加查询条件• List persons = sess.createCriteria(Person.class)• .add( Restrictions.like("name" , "Fritz%") )• .add( Restrictions.between("weight" , minWeight , maxWeight) )• .list();

14.3.2 结果集排序• 可以使用 org.hibernate.criterion.Order 来为查询结果排序,而不是去拼凑 order by 字符串。 Order 类同样提供了工厂方法来构建,另一种方式是通过 Property 类构建,如下代码两种方式都提供了:• // 工厂方法来构建 Order 类• List persons = sess.createCriteria(Person.class)• .add( Restrictions.like("name" , "F%")• .addOrder( Order.asc("name") )• .addOrder( Order.desc("age") )• .setMaxResults(50)• .list();• //Property 类构建 Order 类• List persons = sess.createCriteria(Person.class)• .add( Property.forName("name").like("F%") )• .addOrder( Property.forName("name").asc() )• .addOrder( Property.forName("age").desc() )• .setMaxResults(50)• .list();

14.3.3 条件查询中的关联关系• 可以使用 createCriteria() 非常容易的在互相关联的实体间建立约束。• // 使用 createCriteria 建立实体间的约束• List persons = sess.createCriteria(Person.class)• .add( Restrictions.like("name" , "F%") )• .createCriteria("parts")• .add( Restrictions.like("name" , "F%") )• .list();

14.3.4 使用样例查询• 样例查询顾名思义就是一个一个样例作为条件,把与这个样例有相同属性的实例查询出来。 Hibernate 提供了 org.hibernate.criterion.Example类,允许用户通过一个给定实例来构建一个条件查询。如下代码,构造一个 person 实例作为查询条件,此时版本属性、标识符和关联被忽略。默认情况下值为 null 的属性将被排除。

14.3.5 离线 (detached) 查询和子查询• Hibernate3 提供了一个新的 Criteria : DetachdCriteria 。此类可脱离 Session 实例而存在。这样就可以将某些通用的 Criteria 查询条件进行抽离,每次使用前再与当前的 Session 绑定以获得更好的代码重用效果。• 典型应用如下: DetachedCriteria 类使在一个 session 范围之外创建一个查询,并且可以使用任意的 Session 来执行它。

14.4 直接使用 SQL• Hibernate 还支持使用 SQL 查询,使用 SQL 查询可以利用某些数据库的特性,或者用于将原有的 JDBC 应用迁移到 Hibernate 应用上。使用命名的 SQL 查询还可以将 SQL 语句放在配置文件中配置,从而提高程序的解耦,命名 SQL 查询还可以用于调用存储过程。如果是一个新的应用,通常不要使用 SQL 查询。

14.4.1 使用 SQL 查询• SQL 查询是通过 SQLQuery 接口来表示的, SQLQuery 接口是 Query 接口的子接口,因此完全可以调用 Query 接口的方法:• setFirstResult() ,设置返回结果集的起始点。• setMaxResults() ,设置查询获取的最大记录数。• list() ,返回查询到的结果集。• 但 SQLQuery 比 Query 多了两个重载的方法:• addEntity ,将查询到的记录与特定的实体关联。• addScalar ,将查询的记录关联成标量值。

14.4.2 SQL 语句放入配置文件中• 可以在映射文档中定义查询的名字,然后就可以象调用一个命名的 HQL 查询一样直接调用命名 SQL 查询。在文件中的 SQL 语句,使用 <sql-query> 标签,同样与映射文件中的 class 同一个级别,还有两个元素 <return-join> 和 <load-collection> 。它们是用来连接关联以及将查询定义为预先初始化各个集合的

14.4.3 调用存储过程• Hibernate3 引入了对存储过程查询 (stored procedure) 和函数 (function) 的支持。要求这个存储过程或者函数必须返回一个结果集,作为 Hibernate 能够使用的第一个外部参数。因为存储过程本身完成了查询的全部操作,所以调用存储过程进行的查询无法使用 setFirstResult()/setMaxResults() 进行分页。• 存储过程只能返回一个结果集,如果存储过程返回多个结果集, Hibernate 将仅处理第一个结果集,其他将被丢弃。• 如果在存储过程里设定 SET NOCOUNT ON ,将有更好的性能表现。当然也可以没有该设定。

14.5 数据过滤• 数据过滤不是一种常规的数据查询方法,而是一种整体的筛选方法。数据过滤也可对数据进行筛选,因此,将其放在 Hibernate 的数据查询框架中介绍。如果一旦启用了数据过滤器,则不管数据查询,还是数据加载,该过滤器将自动作用于所有数据,只有满足过滤条件的记录才会被选出来。• 过滤器与定义在类和集合映射文件上的“ where” 属性非常相似。它们的区别是过滤器可以带参数,应用程序可以在运行时决定是否启用指定的过滤器,以及使用什么样的参数值。而映射文件上的“ where” 属性将一直生效,且无法动态传入参数。过滤器的用法很像数据库视图,区别是视图在数据库中已经定义完成,而过滤器则还需在应用程序中确定参数值。

14.6 小结• 数据的检索是数据库最重要的功能之一。在本章中介绍了几种 Hibernate 检索数据的方法。在实际应用中可根据需求灵活应用,除非必须应该尽量避免直接使用 SQL 语句,这将违背 Hibernate的设计初衷,为将来数据库移植也带来隐患。

top related