分类:
2008-11-23 20:22:14
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();
示例代码:
Customer exampleCustomer=new Customer();
exampleCustomer.setAge(21);
List result1 = session.createCriteria(Customer.class).add(
Example.create(exampleCustomer)).list();
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 "
除了以上用于绑定映射类型的参数的方法,Hibernate还提供了以下三个特殊的参数绑定方法。
(1)setEntity()方法:把参数与一个持久化类的实例绑定。(用id关联)
(2)setParameter()方法:绑定任意类型的参数。
(3)setProperties()方法:用于把命名参数与一个对象的属性值绑定。
二.设定查询条件
在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)不区分大小写: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); 。
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 |
三、连接查询
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.START))
.createCriteria("orders")
.add(Expression.like("orderNumber","t",MatchMode.START)).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.START))
.add(Expression.like("o.orderNumber","t",MatchMode.START))
.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.START))
.list();
多对一
"from Order o where o.customer.name like '%hai%'"
QBC 不支持隐式内连接,下边是不正确的,:
List reslut=session.createCriteria(Order.class)
.add(Expression.like("customer.name","t",MatchMode.START))
.list();
对于QBC,必须显式指定内连接查询:
List reslut=session.createCriteria(Order.class)
.createAlias("customer","c")
.add(Expression.like("c.name","t",MatchMode.START))
.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:覆盖映射配置文件的检索策略,在程序中指定延迟检索策略。
四、 报表查询
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.createCriteria(Customer.class).add(exampleCustomer);
List list=criteria.list();
注释:ignoreCase表示比较字符串时忽略大小写;enableLike表示样板对象中所有字符串类型的属性采用模糊比较;excludeZeroes表示样板对象中数字类型的属性为0,就不把它添加到查询语句中。如果某个属性为null,则查询语句不包含此条件。
查询窗口中允许同时指定客户查询条件和订单查询条件,仍然可以使用QBE检索。
Customer customer=....
Example exampleCustomer=Example.create(customer);
exampleCustomer.ignoreCase().enableLike(MatchMode.ANYWHERE);
exampleCustomer.excludeZeroes();
Order order=...
Example exampleOrder=Example.create(order);
exampleOrder.ignoreCase().enableLike(MatchMode.ANYWHERE);
exampleOrder.excludeZeroes();
Criteria criteria=session.createCriteria(Customer.class)
.add(exampleCustomer)
.createCriteria("orders")
.add(exampleOrder);
List list=criteria.list();
2、集合过滤
延迟检索策略――customer.getOrders().iterator() (加载关联对象集合),这种方式的不足:
l 全部加载
l 不能排序
2种办法可以解决上边不足,一种是通过HQL或QBC查询orders集合,还有一种办法就是使用集合过滤。
集合过滤示例:
List result=session.createFilter(customer.getOrders(),
"where this.price>200 order by this.price").list();
Iterator it=result.iterator();
while(it.hasNext()){
Order order =(Order)it.next();
.....
}
Session的createFilter()方法用来过滤集合,它具有以下特定。
l 返回Qurey类型的实例。
l 第一个参数:指定一个持久化对象的集合,这个集合是否已经被初始化并没有关系,但它所属的对象必须处于持久化状态。否则抛出异常。
l 第二个参数:指定过滤条件,它由合法的HQL查询语句组成。
l 不管持久化对象的集合是否已经初始化,Query的list()方法都会执行SQL查询语句,到数据库中检索Order对象。
l 如果对象的集合已经被初始化,为了保证Session的缓存中不会出现OID相同的Order对象,Query的list()方法不会再创建Order对象,仅仅返回已经存在的Order对象的引用。
l 如果没有初始化,Query的list()方法创建相应的对象,但不会初始化所给对象的集合。(仅仅取出符合条件的对象集合,是对象集合的子集)
集合过滤的几个应用:
A、为集合排序或设置约束条件
B、 集合分页
C、 检索集合中对象的某个属性
D、检索数据库中与Customer对象的orders集合中的Order对象的属性(一个或多个)相同的所有Order对象
E、 检索Order对象的lineItems集合中LineItem对象的Item
代码示例:
List result=session.createFilter(customer.getOrders(),
" order by this.price asc")
.setFirstResult(10)
.setMaxResults(5)
.list();
List result=session.createFilter(customer.getOrders(),
"select this.orderNumber").list();
List result=session.createFilter(customer.getOrders(),
"select other from Order other where other.price=this.price").list();
List result=session.createFilter(order.getLineItems(),"select this.item").list();
3、子查询
HQL支持where子句中嵌入查询语句。
from Customer c where 1<(select count(o) from c.orders o) --相关子查询
from Order o where o.price>(select avg(o1.price) from Order o1) --无关
关于子查询的用法说明:
(1)、子查询可以分为相关子查询和无关子查询。
(2)、依赖底层数据库对子查询的支持能力。
(3)、如果子查询语句返回多条记录,可以用一下关键字来衡量。
l all:表示子查询语句返回的所有记录。
l any:任意一条记录。
l some:与“any”等价。
l in:与“=any”等价。
l exists:至少返回一条记录。
例:订单的价格都不小于100的客户
from Customer c where 100>all (select o.price from c.orders o)
有一条订单的价格小于100的客户
from Customer c where 100>any (select o.price from c.orders o)
有一条订单的价格等于100的客户
from Customer c where 100=some (select o.price from c.orders o)
或
from Customer c where 100=any (select o.price from c.orders o)
或
from Customer c where 100 in (select o.price from c.orders o)
至少有一条订单的客户
from Customer c where exsist (from c.orders)
(4)、如果子查询语句查询的是集合,HQL提供了所写语法
Iterator it=session.createQurey(
"from Customer c where :order in element(c.orders)")
.setEntity("order",order)
.list() .iterator();
element(c.orders)等价于(from c.orders)
HQL提供了一组操纵集合的函数或者属性。
l size()函数或size属性:获取集合中元素的数目。
l minIndex()函数或minIndex属性:对于建立了索引的集合,获取最小的索引。
l maxIndex()函数或maxIndex属性:对于建立了索引的集合,获取最大的索引。
l minElement()函数或minElement属性:对于包含基本类型元素的集合,获得集合中取值最小的元素。
l maxElement()函数或maxElement属性:对于包含基本类型元素的集合,获得集合中取值最大的元素。
l elements()函数:获得集合中所有元素。
如:订单数目大于零的客户
from Customer c where c.orders.size>0
或者 from Customer c where size(c.orders)>0
4、本地SQL查询
Hibernate本地SQL查询提供了支持,为了把SQL查询返回的关系数据映射为对象,需要在SQL查询语句中为字段指定别名。
String sql1 = "select cs.ID as {c.id},cs.name as {c.name} "
+ "from CUSTOMERS cs where cs.ID=1";
Query query = session.createSQLQurey(sql1, "c", Customer.class);
或
String sql1 = "select {c.*} "
+ "from CUSTOMERS c where c.ID=1";
Query query = session.createSQLQurey(sql1, "c", Customer.class);
多个不同的对象情况
String sql1 = "select {c.*},{o.*} from CUSTOMERS c inner join ORDERS o"
+ " where c.ID=o.CUSTOMER_ID";
Query query = session.createSQLQurey(sql1, new String[] { "c", "o" },
new Class[] { Customer.class, Order.class });
在程序中嵌入本地SQL语句会增加维护程序代码的难度,如果数据库表的结构发生变化,必须修改相应的程序代码,因此更合理的方式是把SQL查询语句放到映射文件中:
select {c.*},{o.*} from CUSTOMERS c inner join ORDERS o
where c.ID=o.CUSTOMER_ID
]]>
<return alias="c" class="Customer">
<return alias="o" class="Order">
六、查询性能优化
1、 降低访问数据库的频率,减少select语句的数目。实现手段包括:
l 使用迫切左外连接或迫切内连接检索策略。
l 对延迟加载或立即加载策略设置批量检索数目。
l 使用查询缓存。
2、避免多余加载程序不需要访问的数据。实现手段包括:
l 使用延迟检索策略。
l 使用集合过滤。
3、 避免报表查询数据占用缓存。实现手段为利用投影查询功能,查询出实体的部分属性。
4、减少select语句中的字段,从而降低访问数据库的数据量。实现手段为利用Query的iterate()方法。
iterate()方法:
Query接口的iterate()方法和list()方法都能执行SQL查询语句。
list()首先检索ID字段,然后根据ID字段到Hibernate第一缓存以及第二级缓存中查找匹配的对象,如果存在,就直接把它加入到查询结果集中,否则就只想额外的select语句,根据ID字段数据库中检索该对象。
查询缓存
查询缓存适应以下场合:
l 在应用程序运行时经常使用的查询语句。
l 很少对与查询语句关联的数据库数据进行插入、删除或更新操作。
查询语句启用查询缓存的步骤如下:
A、配置第二级缓存。
B、 在Hibernate的hibernate.properties配置文件中设置查询缓存属性:
hibernate.cache.use_query_cache=true
C、启用查询缓存。调用Query接口的setCacheable(true)方法。
结束!