Hibernate 提供以下几种检索对象的方式。
l 导航对象图检索方式。(根据已经加载的对象,导航到其他对象。)
l OID 检索方式。(按照对象的 OID 来检索对象。)
l HQL 检索方式。(使用面向对象的 HQL 查询语言。)
l QBC 检索方式。(使用 QBC(Qurey By Criteria) API 来检索对象。)
l 本地 SQL 检索方式。(使用本地数据库的 SQL 查询语句。)
一、 Hibernate 的检索方式简介
1 、 HQL 检索方式
HQL ( Hibernate Query Language )是面向对象的查询语言,它和 SQL 查询语言有些相识。在 Hibernate 提供的各种检索方式中, HQL 是使用最广的一种检索方式。它具有以下功能:
l 在查询语句中设定各种查询条件。
l 支持投影查询,即仅检索出对象的部分属性。
l 支持分页查询。
l 支持分组查询,允许使用 having 和 group by 关键字。
l 提供内置聚集函数,如 sum() 、 min() 和 max() 。
l 能够调用用户定义的 SQL 函数。
l 支持子查询,即嵌入式查询。
l 支持动态绑定参数。
Session 类的 find 方法及 Qurey 接口都支持 HQL 检索方式。区别在于,前者只是执行一些简单 HQL 查询语句的便捷方法,它不具有动态绑定参数的功能,而且在将来新的 Hibernate 版本中,有可能淘汰 find 方法;而 Qurey 接口才是真正的 HQL 查询接口,它提供了以上列出的各种查询功能。
注: Qurey 接口支持方法链编程风格,它的 set 方法都返回自身实例,而不是返回 void 类型。方法链编程风格能使程序代码更加简洁。
示例代码:
Query query = session.createQuery("from Customer as c where " +"c.name=:customerName and c.age=:customerAge");
// 动态绑定参数
query.setString("customerName", "Test"); query.setInteger("customerAge", 21);
// 执行检索 List result = query.list();
// 方法链编程风格 List result1 = session.createQuery( "from Customer as c where c.name=:customerName" + " and c.age=:customerAge").setString( "customerName", "Test").setInteger("customerAge", 21) .list();
2 、 QBC ( Qurey By Criteria )检索方式
采用 HQL 检索方式时,在应用程序中需要定义基于字符串形式的 HQL 查询语句。 QBC API 提供了检索对象的另一种方式,它主要由 Criteria 接口、 Criterion 接口和 Expression 类组成,它支持在运行时动态生成查询语句。
示例代码:
Criteria criteria = session.createCriteria(Customer.class);
Criterion criterion1 = Expression.like("namr", "T%");
Criterion criterion2 = Expression.eq("age", new Integer(21));
criteria = criteria.add(criterion1);
criteria = criteria.add(criterion2);
// 执行检索 List result = criteria.list();
// 方法链编程风格 List result1 = session.createCriteria(Customer.class).add(Expression.like("namr", "T%")).add(Expression.eq("age", new Integer(21))).list();
Hibernate 还提供了 QBE(Qurey By Example) 检索方式,它是 QBC 的子功能。 QBE 允许先创建一个随想模板,然后检索出和这个样板相同的对象。
示例代码:
Customer exampleCustomer=new Customer();
exampleCustomer.setAge(21);
List result1 = session.createCriteria(Customer.class).add( Example.create(exampleCustomer)).list();
QBE 的功能不是特别强大,仅在某些场合下有用。一个典型的使用场合就是在查询窗口中让用户输入一系列的查询条件,然后返回匹配的对象。 QBE 只支持“”和“”比较运算符,无法不大区间值,及其或的匹配。在这种情况下,还是采用 HQL 检索方式或 QBC 检索方式。
3 、 SQL 检索方式
采用 HQL 或 QBC 检索方式时, Hibernate 生成标准的 SQL 查询语句,使用于所有的数据库平台,因此这两种检索方式都是跨平台的
有的应用程序可能需要根据底层数据库的 SQL 方言,来生成一些特殊的查询语句。在这种情况下,可以利用 Hibernate 提供的 SQL 检索方式。
示例代码:
Query query = session.createSQLQuery("select {c.*} from CUSTOMER as c where c.NAME like :
customerName and c.AGE=:customerAge");
// 动态绑定参数 query.setString("customerName", "Test"); query.setInteger("customerAge", 21);
// 执行检索 List result = query.list();
4 、使用别名
通过 HQL 检索一个类时,如果查询语句的其他地方需要引用它,应该为这个类指定一个别名, as 关键字用于设定别名,也可以将 as 关键字省略。
QBC 检索不需要由应用程序显式指定类的别名, Hibernate 会自动把查询语句中的跟结点实体赋予别名“ this ”。
5 、多态查询
HQL 和 QBC 都支持多态查询,多态查询是指查询出当前类及所有子类的实例。多态查询对接口也使用。 Hibernate 不仅对 from 子句显式指定的类进行多态查询,而且对其他关联的类也会进行多态查询。
6 、对查询结果排序
HQL 和 QBC 都支持对查询结果进行排序。
query = session.createQuery("from Customer as c order by c.name");
// 排序 criteria.addOrder(Order.asc("name")); criteria.addOrder(Order.desc("age"));
7 、分页查询
Query 和 Criteria 接口都提供了用于分页显式查询结果的方法。
l setFirstResult(int firstResult) :设置从那个对象开始检索,参数表示这个对象在查询结果中的索引位置,索引位置的起始值为 0 。默认从 0 检索。
l setMaxResult(int maxResults) :设置一次最多检索出的对象数目。默认检索所有。
示例代码:
criteria = criteria.add(criterion1);
criteria = criteria.add(criterion2);
criteria.setFirstResult(0);
criteria.setMaxResults(10); // 执行检索
List result = criteria.list();
query.setString("customerName", "Test");
query.setInteger("customerAge", 21);
query.setFirstResult(0);
query.setMaxResults(10); // 执行检索
List result = query.list();
8 、检索单个对象
Query 和 Criteria 接口都提供了以下用于查询语句并返回查询结果的方法。
l list() 方法:返回一个 List 类型的查询结果。
l uniqueResult() 方法:返回单个对象。
注: Query 接口还提供了一个 iterate() 方法,它和 list() 方法一样,能返回所有满足条件的持久化对象,但是两者使用不同的 SQL 查询语句。
示例代码:
// 单个检索 Customer customer = (Customer) session.createQuery("from Customer as c order by c.name").setMaxResults(1) .uniqueResult();
// 单个检索 Customer customer = (Customer) session.createCriteria(
Customer.class).setMaxResults(1).uniqueResult();
如果明明知道一个对象,可以不调用 setMaxResults(1) 方法。
9 、在 HQL 查询语句中绑定参数
A 、按参数名字绑定。咋 HQL 中定义命名参数以“ : ”开头。
B 、按照参数位置绑定。在 HQL 查询语句中用“ ? ”来定义参数的位置。
示例代码:
Query query = session.createQuery("from Customer as c where " + "c.name=? and c.age=?"); // 动态绑定参数
query.setString(0, "Test");
query.setInteger(1, 21);
除了以上用于绑定映射类型的参数的方法, Hibernate 还提供了以下三个特殊的参数绑定方法。
( 1 ) setEntity() 方法:把参数与一个持久化类的实例绑定。(用 id 关联)
( 2 ) setParameter() 方法:绑定任意类型的参数。
( 3 ) setProperties() 方法:用于把命名参数与一个对象的属性值绑定。
10 、在映射文件中定义命名查询语句(略)
Hibernate 的检索方式(二)
二.设定查询条件
在 where 子句中给出的是对象的属性名,而不是字段名。
HQL 和 QBC 支持的各种运算
运算类型 HQL 运算符 QBC 运算符 含义
比较运算 = Expression.eq() 等于
<> Expression.not(Expression.eq()) 不等于
> Expression.gt() 大于
>= Expression.ge() 大于等于
< Expression.lt() 小于
<= Expression.le() 小于等于
is null Expression.isNull() 等于空值
is not null Expression.isNotNull() 非空值
范围运算 in ( 列表 ) Expression.in() 等于列表中的某一个值
not in ( 列表 ) Expression.not(Expression.in()) 不等于列表中的任意一个值
between 值 1 and 值 2 Expression.between() 大于等于值 1 并且小于等于值 2
not between 值 1 and 值 2 Expression.not(Expression.between()) 小于值 1 或者大于值 2
字符串模式匹配 like Expression.like() 字符串模式匹配
逻辑运算 and Expression.add() 或者 Expression.conjunction() 逻辑与
or Expression.or() 或者 Expression.disjunction() 逻辑或
not Expression.not() 逻辑非
1 、比较运算
( 1 )不区分大小写: HQL 使用 lower() 或者 upper() 来实现(如:”… lower(c.name)= ’ tom ’”);
QBC 使用 .ignoreCase() 来实现(如: Expression.eq( “” , ”” ) .ignoreCase() )。
注:在 HQL 中,可以调用 SQL 函数。 lower() 转小写, upper() 转大写。
QBC 不支持直接调用 SQL 函数。
( 2 ) HQL 查询支持数学运算表达式,而 QBC 不支持。
2 、范围运算
HQL 中的 in 示例: c.name in ( ‘ aa ’ , ’ bb ’ ) ;
QBC 中的 in 示例: String[] names={ ‘ aa ’ , ’ bb ’ }; Expression.in( ‘ name ’ ,names); 。
Hibernate 的检索方式(二)续
3 、字符串模式匹配
HQL 和 QBC 通用:字符串模式中的通配符
通配符名称 通配符 作用
百分号 % 匹配任意类型且任意长度(长度可以为 0 )的字符串,如果是中文,需要两个百分号,即“ %% ”
下划线 _ 匹配单个任意字符,常用来限制字符串表达式的长度
QBC : MatchMode 类包含的各个静态常量实例
匹配模式 举例
MatchMode.START Expression.like(“name”,”y”, MatchMode.START)
姓名以 y 开头
MatchMode.END Expression.like(“name”,”y”, MatchMode. END)
姓名以 y 结尾
MatchMode.ANYWHERE Expression.like(“name”,”y”, MatchMode. ANYWHERE)
姓名中包含 y
MatchMode.EXACT Expression.like(“name”,”y”, MatchMode. EXACT)
精确匹配,姓名必须为 y
4 、逻辑运算
Hibernate 的检索方式(三)
三、连接查询
HQL 和 QBC 支持的各种连接类型
在程序中指定的链接查询类型 HQL 语法 QBC 语法 使用范围
内连接 inner join 或者 join Criteria.createAlias() 适用于有关联的持久化类,并且在映射文件中对这种关联关系作了映射。
迫切内连接 inner join fetch 或者 join fetch 不支持
隐式内连接 不支持
左外连接 left outer join 或者 left join 不支持
迫切左外连接 left outer join fetch 或者 left join fetch FetchMode.EAGER
右外连接 right outer join 或者 right join 不支持
交叉连接 ClassA,ClassB 不支持 适用于不存在关联关系的持久化类
1 、默认情况下关联级别的运行时检索策略
采用配置文件中设置的检索策略,但有一个例外,那就是 HQL 会忽略映射文件设置的迫切左外连接策略,改用立即检索。
2 、迫切左外连接
显式指定对象的关联关系为迫切左外连接检索策略,可以覆盖映射文件中指定的检索策略。
例 HQL
"from Customer c left join fetch c.orders o where c.name like 't%'"
+" o.name like 't%'"
例 QBC
List reslut=session.createCriteria(Customer.class) .setFetchMode("orders",FetchMode.EAGER) .add(Expression.like("name","t",MatchMode.START))
.list();
当使用迫切左外连接检索策略时,查询结果中可能会包含重复元素,可以通过一个 HashSet 来过滤重复元素: List result= … .list(); HashSet set=new HashSet(result);
Hibernate 允许在一条查询语句中迫切左外连接多个多对一或一对一关联的类。
List reslut=session.createCriteria(A.class) .setFetchMode("this.b",FetchMode.EAGER) .setFetchMode("this.c",FetchMode.EAGER) .add(Expression.isNotNull("this.b")) .add(Expression.isNotNull("this.c"))
.list();
当存在传递关联时,可以通过 HQL 来同时迫切左外连接关联类和依赖关联类,但 QBC 无法表达这种形式的迫切左外连接。
3 、左外连接
使用左外连接查询时,将根据映射文件的配置来决定关联的检索策略。
4 、内连接
QBC 也支持内连接查询
List reslut=session.createCriteria(Customer.class) .add(Expression.like("name","t",MatchMode.SRART)) .createCriteria("orders") .add(Expression.like("orderNumber","t",MatchMode.SRART))
.list();
默认情况下, QBC 只检索出 Customer 对象,以上代码等同于以下 HQL 查询语句:
"select c from Customer c join c.orders o where c.name like 't%'"
+ " and o.orderNumber like 't%'";
createAlias() 方法为关联属性(集合)赋予别名。如果希望 QBC 返回的集合也包含成对的 Customer 和 Order 对象,可以调用 returnMaps() 方法:
List reslut=session.createCriteria(Customer.class) .createAlias("orders","o") .add(Expression.like("this.name","t",MatchMode.SRART)) .add(Expression.like("o.orderNumber","t",MatchMode.SRART)) .returnMaps()
.list();
采用内连接查询时, HQL 与 QBC 有不同的默认行为,前者检索成对的对象,后者仅检索出要检索的单个对象(不包含关联的对象)。
5 、迫切内连接
显式指定对象的关联关系为迫切内连接检索策略,可以覆盖映射文件中指定的检索策略。
6 、隐式内连接
一对一
"from Cunstomer c where c.homeAddress.provice like '%hai%'"
List reslut=session.createCriteria(Customer.class) .add(Expression.like("homeAddress.provice","t",MatchMode.SRART))
.list();
多对一
"from Order o where o.customer.name like '%hai%'"
QBC 不支持隐式内连接,下边是不正确的,:
List reslut=session.createCriteria(Order.class) .add(Expression.like("customer.name","t",MatchMode.SRART))
.list();
对于 QBC ,必须显式指定内连接查询:
List reslut=session.createCriteria(Order.class) .createAlias("customer","c") .add(Expression.like("c.name","t",MatchMode.SRART))
.list();
一对多或多对多
隐式内连接不适用。
7 、右外连接
8 、使用 SQL 风格的交叉连接和隐式内连接
HQL 支持 SQL 风格的交叉连接查询。如: from Customer c, Order o
在 SQL 语言中,显式内连接查询的语句使用 inner join 关键字,并且用 on 字句设定连接条件,形式为:
"select * from CUSTOMER c inner join ORDER o on c.ID=o.CUSTOMER_ID"
隐式内连接查询语句不包含关键字阿,并且用 where 字句设定连接条件:
"select * from CUSTOMER c ,ORDER o where c.ID=o.CUSTOMER_ID"
9 、关联级别运行是的检索策略
( 1 )没有显式指定,使用配置文件的,但有一个例外,那就是 HQL 会忽略映射文件设置的迫切左外连接策略,改用立即检索。
( 2 )如果显式指定,就会覆盖映射文件配置的检索策略。在 HQL 查询语句中显式指定检索策略包括以下内容。
l left join fetch
l inner join fetch
QBC 通过 FetchMode 类来显式指定检索策略,有以下 3 个静态实例。
l FetchMode.DEFAULT :默认,采用配置;
l FetchMode.EAGER :覆盖,指定迫切左外连接检索策略;
l FetchMode.LAZY :覆盖映射配置文件的检索策略,在程序中指定延迟检索策略。
Hibernate 的检索方式(四)
四、 报表查询
1 、投影查询
select c from Customer c ……
select c.name,c.age from Customer c ……
( 1 )动态实例化查询结果
select new com.CustomerRow(c.id,c.name,c.age) from Customer c ……
注: CustomerRow 类不需要是持久化类,因此不必创建它的对象-关系映射文件,它紧紧用于把 select 语句查询出来的关系数据包装为 Java 对象。
( 2 )过滤查询结果中的重复元素
select distinct c.name Customer c ……
2 、使用聚集函数
在 HQL 查询语句中可以调用以下聚集函数
l count() :统计记录数
l min()
l max()
l sum()
l avg() :求平均值
3 、分组查询( group by … (having … ) )
select c.name,count(c) from Customer c group by c.name
list() 结果: n 个对象数组类型的元素,每个对象数组对应查询结果中的一条记录。如
Iterator it = session.createQurey( "select c.name,count(c) from Customer c group by c.name") .list().iterator(); while (it.hasNext()) { Object[] pair = (Object[]) it.next(); String name = (String) pair[0]; Integer count = (Integer) pair[1];
}
4 、优化报表查询的性能
报表查询的特点:通常会处理大量数据;一般只涉及对数据的读操作,而不会修改数据。
当 select 语句仅仅选择查询持久化类的部分属性时, Hibernate 返回的查询结果为关系数据(不会占用 Session 的缓存),而不是持久化对象(位于 Session 缓存中)。
如果采用 select 语句查询,能提高报表查询的性能,只要应用程序不在引用这些数据,它们占用的内存就会被释放。当采用 from 类型的 HQL 语句查询出持久化对象,会导致大量的持久化对象一直位于 Session 的缓存中,而且 Session 还比讯负责这些对象与数据库的同步。
对于 select 语句查询,可以定义一个 JavaBean 来包装查询结果中的关系数据,使应用程序仍旧能够面向对象的方式来访问查询结果。
五、高级查询技巧
1 、动态查询
HQL 和 QBC 都能完成许多相同的任务,相比之下, HQL 能够直观地表达复杂的查询语句,而 QBC 表达复杂的查询语句很麻烦。
如果在程序运行前就明确了查询语句的内容(也称静态查询),应该优先考虑 HQL 查询方式。但是,如果只有在程序运行时才能明确查询语句的内容(也称动态查询), QBC 比 HQL 更加的方便。
QBE 的模糊匹配示例:
Customer customer=.... Example exampleCustomer=Example.create(customer); exampleCustomer.ignoreCase().enableLike(MatchMode.ANYWHERE); exampleCustomer.excludeZeroes(); Criteria criteria=session.crea