290 likes | 422 Views
JavaEE 程序设计 第12讲 Hibernate查询体系. 课程结构 教学任务 培养目标 教学时间与方式. 课程结构 :. 教学任务 :. 培养目标 : 通过对 Hibernate 的各种数据查询方法的学习,掌握 Hibernate 的查询方法,并在软件项目中加以运用。 教学时间与方式 : 理实一体化, 6 学时. 12.1 Hibernate查询体系 Hibernate提供了异常强大的查询体系,使用Hibernate有多种查询方式。 可以选择使用Hibernate的HQL查询, 或者使用条件查询, 甚至可以使用原生的SQL查询语句,
E N D
课程结构 教学任务 培养目标 教学时间与方式
培养目标: 通过对Hibernate的各种数据查询方法的学习,掌握Hibernate的查询方法,并在软件项目中加以运用。 教学时间与方式: 理实一体化,6学时
12.1 Hibernate查询体系 • Hibernate提供了异常强大的查询体系,使用Hibernate有多种查询方式。 • 可以选择使用Hibernate的HQL查询, • 或者使用条件查询, • 甚至可以使用原生的SQL查询语句, • 此外还提供了一种数据过滤功能, • 这些都可用于筛选目标数据。 • HQL是Hibernate Query Language的缩写,HQL的语法很像SQL的语法,但HQL是一种面向对象的查询语言。因此,SQL的操作对象是数据表和列等数据对象,而HQL的操作对象是类、实例、属性等。 • HQL是完全面向对象的查询语言,因此可以支持继承和多态等特征。
12.1 Hibernate查询体系 HQL查询依赖于Query类,每个Query实例对应一个查询对象。使用HQL查询可按如下步骤进行: (1)获取Hibernate Session对象; (2)编写HQL语句; (3)以HQL语句作为参数,调用Session的createQuery方法创建查询对象; (4)如果HQL语句包含参数,调用Query的setXxx方法为参数赋值; (5)调用Query对象的list等方法遍历查询结果。 通过上面的示例程序,可看出查询步骤基本相似。Query对象可以连续多次设置参数,这得益于Hibernate Query的设计。 通常,setXxx方法的返回值都是void,但Hibernate Query的setXxx方法返回值是Query本身。因此,程序通过Session创建Query后,直接多次调用setXxx方法为HQL语句的参数赋值,再直接调用list方法返回查询到的全部结果即可。
12.2 常用的查询方法 12.2.1 执行查询 HQL和原生SQL(native SQL)查询要通过为org.hibernate.Query的实例来表达。 这个接口提供了参数绑定、结果集处理以及运行实际查询的方法。 你总是可以通过当前Session获取一个Query对象 一个查询通常在调用list()时被执行,执行结果会完全装载进内存中的一个集合(collection)。 查询返回的对象处于持久(persistent)状态。如果你知道的查询只会返回一个对象,可使用list()的快捷方式uniqueResult()。 注意,使用集合预先抓取的查询往往会返回多次根对象(他们的集合类都被初始化了)。你可以通过一个集合来过滤这些重复对象。
12.2 常用的查询方法 12.2.2 迭代式获取结果(Iterating results) 某些情况下,你可以使用iterate()方法得到更好的性能。 这通常是你预期返回的结果在session,或二级缓存(second-level cache)中已经存在时的情况。 如若不然,iterate()会比list()慢,而且可能简单查询也需要进行多次数据库访问: iterate()会首先使用1条语句得到所有对象的持久化标识(identifiers),再根据持久化标识执行n条附加的select语句实例化实际的对象。
12.2 常用的查询方法 12.2.3 返回元组(tuples)的查询 Hibernate查询有时返回元组(tuples),每个元组(tuples)以数组的形式返回: (译注:元组(tuples)指一条结果行包含多个对象)。
12.2 常用的查询方法 12.2.4 标量(Scalar)结果 查询可在select从句中指定类的属性,甚至可以调用SQL统计(aggregate)函数。 属性或统计结果被认定为"标量(Scalar)"的结果(而不是持久(persistent state)的实体)。
12.2 常用的查询方法 • 12.2.5 绑定参数 • 接口Query提供了对命名参数(named parameters)、JDBC风格的问号(?)参数进行绑定的方法。 不同于JDBC,Hibernate对参数从0开始计数。 命名参数(namedparameters)在查询字符串中是形如:name的标识符。 命名参数(named parameters)的优点是: • 命名参数(named parameters)与其在查询串中出现的顺序无关 • 它们可在同一查询串中多次出现 • 它们本身是自我说明的
12.2 常用的查询方法 12.2.6 外置命名查询(Externalizing named queries) 你可以在映射文件中定义命名查询(named queries)。 (如果你的查询串中包含可能被解释为XML标记(markup)的字符,别忘了用CDATA包裹起来。) 参数绑定及执行以编程方式(programatically)完成:
12.3 HQL 12.3.1 HQL查询的from子句 from子句是最简单的HQL语句,也是最基本的HQL语句。from关键字后紧跟持久化类的类名。例如:from Person表明从Person持久化类中选出全部的实例。 大部分时候,推荐为该Person的每个实例起别名。例如:from Person as p。 在上面的HQL语句中,Person持久化类中的实例的别名为p,既然 p是实例名,因此也应该遵守Java的命名规则:第一个单词的首字母小写,后面每个单词的首字母大写。命名别名时,as关键字是可选的,但为了增加可读性,建议保留。 from后还可同时出现多个持久化类,此时将产生一个笛卡儿积或跨表的连接。
12.3 HQL 12.3.2 HQL查询的select子句 select子句用于确定选择出的属性,当然select选择的属性必须是from后持久化类包含的属性。例如:select p.name from Person as p。 select可以选择任意属性,不仅可以选择持久化类的直接属性,还可以选择组件属性包含的属性,例如: select p.name.firstName from Person as p select也支持将选择出的属性存入一个List对象中,例如: select new list(p.name , p.address) from Person as p 甚至可以将选择出的属性直接封装成对象,例如: select new ClassTest(p.name , p.address) from Person as p 前提是ClassTest支持p.name和p.address的构造器。
12.3 HQL 12.3.3 HQL查询的聚集函数 HQL也支持在选出的属性上,使用聚集函数。HQL支持的聚集函数与SQL完全相同,有如下5个: ● avg,计算属性平均值。 ● count,统计选择对象的数量。 ● max,统计属性值的最大值 ● min,统计属性值的最小值。 ● sum,计算属性值的总和。 例如: select count(*) from Person select max(p.age) from Person as p select子句还支持字符串连接符、算术运算符以及SQL函数。如: select p.name || "" || p.address from Person as p select子句也支持使用distinct和all关键字,此时的效果与SQL中的效果完全相同。
12.3 HQL 12.3.4 多态查询 HQL语句被设计成能理解多态查询,from后跟的持久化类名,不仅会查询出该持久化类的全部实例,还会查询出该类的子类的全部实例。 如下面的查询语句: from Person as p 该查询语句不仅会查询出Person的全部实例,还会查询出Person的子类,如Teacher的全部实例,前提是Person和Teacher完成了正确的继承映射。 HQL支持在from子句中指定任何Java类或接口,查询会返回继承了该类的持久化子类的实例或返回实现该接口的持久化类的实例。下面的查询语句返回所有被持久化的对象: from java.lang.Object o 如果Named接口有多个持久化类,下面的语句将返回这些持久化类的全部实例: from Named as n 注意:后面的两个查询将需要多个SQL SELECT语句,因此无法使用order by子句对结果集进行排序,从而,不允许对这些查询结果使用Query.scroll()方法。
12.3 HQL 12.3.5 HQL查询的where子句 where子句用于筛选选中的结果,缩小选择的范围。如果没有为持久化实例命名别名,可以直接使用属性名引用属性。 如下面的HQL语句: from Person where name like 'tom%' 上面HQL语句与下面的语句效果相同: from Person as p where p.name like "tom%" 在后面的HQL语句中,如果为持久化实例命名了别名,则应该使用完整的属性名。两个HQL语句都可返回name属性以tom开头的实例。 复合属性表达式加强了where子句的功能,例如如下HQL语句: from Cat cat where cat.mate.name like "kit%" 该查询将被翻译成为一个含有内连接的SQL查询,翻译后的SQL语句如下: select * from cat_table as table1 cat_table as table2 where table1.mate = table2.id and table1.name like "kit%"
12.3 HQL 12.3.6 表达式 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等。 ● 简单的case、case ... when ... then ... else ... end和case、case when ... then ... else ... end等。 ● 字符串连接符value1 || value2或使用字符串连接函数concat(value1 , value2)。 ● 时间操作函数current_date()、current_time()、current_timestamp()、second()、minute()、hour()、day()、month()、year()等。 ● HQL还支持EJB-QL 3.0所支持的函数或操作substring()、trim()、lower()、upper()、length()、locate()、abs()、 sqrt()、bit_length()、coalesce()和nullif() ● 还支持数据库的类型转换函数,如cast(... as ...),第二个参数是Hibernate的类型名,或者extract(... from ...),前提是底层数据库支持ANSI cast() 和extract()。 ● 如果底层数据库支持如下单行函数sign()、trunc()、rtrim()、sin()。则HQL语句也完全可以支持。
12.3 HQL 12.3.7 group by子句 返回聚集值的查询可以对持久化类或组件属性的属性进行分组,分组所使用的group by子句。看下面的HQL查询语句: select cat.color, sum(cat.weight), count(cat) from Cat cat group by cat.color having子句用于对分组进行过滤,如下: select cat.color, sum(cat.weight), count(cat) from Cat cat group by cat.color having cat.color in (eg.Color.TABBY, eg.Color.BLACK) 注意:having子句用于对分组进行过滤,因此having子句只能在有group by子句时才可以使用,没有group by子句,不能使用having子句。 Hibernate的HQL语句会直接翻译成数据库SQL语句。因此,如果底层数据库支持的having子句和group by子句中出现一般函数或聚集函数,HQL语句的having子句和order by 子句中也可以出现一般函数和聚集函数。
12.3 HQL 12.3.8 fetch关键字 对于集合属性,Hibernate默认采用延迟加载策略。例如,对于持久化类Person,有集合属性scores。加载Person实例时,默认不加载scores属性。如果Session被关闭,Person实例将无法访问关联的scores属性。 为了解决该问题,可以在Hibernate映射文件中取消延迟加载或使用fetch join,例如: from Person as p join p.scores 上面的fetch语句将会初始化person的scores集合属性。 如果使用了属性级别的延迟获取,可以使用fetch all properties来强制Hibernate立即抓取那些原本需要延迟加载的属性,例如: from Document fetch all properties order by name from Document doc fetch all properties where lower(doc.name) like '%cats%'
12.4 条件查询 条件查询是更具面向对象特色的数据查询方式。条件查询可通过如下3个类完成: ● Criteria,代表一次查询。 ● Criterion,代表一个查询条件。 ● Restrictions,产生查询条件的工具类。 执行条件查询的步骤如下: (1)获得Hibernate的Session对象。 (2)以Session对象创建Criteria对象。 (3)增加Criterion查询条件。 (4)执行Criteria的list等方法返回结果集。 在条件查询中,Criteria接口代表一次查询,该查询本身不具备任何的数据筛选功能,Session调用createCriteria(Class clazz)方法对某个持久化类创建条件查询实例。
12.5 SQL查询 Hibernate还支持使用SQL查询,使用SQL查询可以利用某些数据库的特性,或者用于将原有的JDBC应用迁移到Hibernate应用上。使用命名的SQL查询还可以将SQL语句放在配置文件中配置,从而提高程序的解耦,命名SQL查询还可以用于调用存储过程。如果是一个新的应用,通常不要使用SQL查询。 SQL查询是通过SQLQuery接口来表示的,SQLQuery接口是Query接口的子接口,因此完全可以调用Query接口的方法: ● setFirstResult(),设置返回结果集的起始点。 ● setMaxResults(),设置查询获取的最大记录数。 ● list(),返回查询到的结果集。 但SQLQuery比Query多了两个重载的方法: ● addEntity,将查询到的记录与特定的实体关联。 ● addScalar,将查询的记录关联成标量值。
执行SQL查询的步骤如下: (1)获取Hibernate Session对象; (2)编写SQL语句; (3)以SQL语句作为参数,调用Session的createSQLQuery方法创建查询对象; (4)如果SQL语句包含参数,调用Query的setXxx方法为参数赋值; (5)调用SQLQuery对象的addEntity或addScalar方法将选出的结果与实体或标量值关联; (6)调用Query的list方法返回查询的结果集。
12.5 SQL查询 12.5.1 命名SQL查询 可以将SQL语句不放在程序中,而放在配置文件中,这种方式以松耦合的方式配置SQL语句,可以提高程序解耦。 在Hibernate的映射文件中定义查询名,然后确定查询所用的SQL语句,然后就可以直接调用该命名SQL查询。在这种情况下,不需要调用addEntity()方法,因为在配置命名SQL查询时,已经完成了查询结果与实体的关联。 下面是命名SQL查询的配置片段: <!-- 每个sql-query元素定义一个命名SQL查询 --> <sql-query name="mySqlQuery"> <!-- 关联返回的结果与实体类 --> <return alias="s" class="Student"/> <!-- 定义命名SQL查询的SQL语句 --> SELECT {s.*} from student s WHERE s.name like'杨海华' </sql-query> sql-query元素是hibernate-mapping元素的子元素。
12.5 SQL查询 12.5.2 调用存储过程 Hibernate 3增加了存储过程的支持,该存储过程只能返回一个结果集。 下面是Oracle 9i的存储过程示例: CREATE OR REPLACE FUNCTION selectAllEmployments RETURN SYS_REFCURSOR AS st_cursor SYS_REFCURSOR; BEGIN OPEN st_cursor FOR SELECT EMPLOYEE, EMPLOYER, STARTDATE, ENDDATE, REGIONCODE, EID, VALUE, CURRENCY FROM EMPLOYMENT; RETURN st_cursor; END;
调用存储过程还有如下需要注意的地方: ● 因为存储过程本身完成了查询的全部操作,所以调用存储过程进行的查询无法使用setFirstResult()/setMaxResults()进行分页。 ● 存储过程只能返回一个结果集,如果存储过程返回多个结果集,Hibernate将仅处理第一个结果集,其他将被丢弃。 ● 如果在存储过程里设定SET NOCOUNT ON,将有更好的性能表现。当然也可以没有该设定。
总结: 1. HQL查询的一般用法 2. HQL语法的简单介绍 3. 条件查询 4. SQL查询