Hibernate检索方式(二)
2015-08-11 12:23
316 查看
一:隐式内内连接
在HQL查询语句中,如果对Customer类赋予别名’c’,就可以通过c.name的形式访问name属性,还可以通过c.homeAddress.provice的形式访问homeAddress组件的
Province属性。
Tips:Customer类的homeAddress(表示家庭地址)属性为Address类型,Customer
类与Address类之间为组成关系。在Customer.hbm.xml文件中,用<component>
元素来映射homeAddress属性。
下面HQL查询语句通过o.customer.name 的形式访问与Order关联的Customer对象的name属性:
以上程序代码中的HQL语句尽管没有使用join关键字,其实指明使用内连接查询,它实际对应的SQL语句为:
在select子句中也可以使用隐式内连接,如:
QBC不支持隐式内连接
隐式内连接适合用于多对一和一对一的关联,但适用于一对多或多对多关联。
如:
假定有3个类,它们的关联关系如下图所示,类A和类B以及类B和类c都是多对一关联关系。
图:
二:关联级别运行时的检索策略
(1):如果在HQL或QBC程序代码中没有显示指定检索策略,将使用映射文件中配置的检索策略。但是有个例外,即HQL总是忽略映射文件中设置的迫切左外连接检索策略。也就是说,即使映射文件中设置了迫切左外连接检索策略,如果HQL查询语句没有显示指定这种策略,那么HQL仍然采用延迟检索策略。因此,如果希望HQL采用迫切左外连接检索策略,就必须在HQL查询语句中显示指定它。
(2)如果在HQL或QBC程序代码中显示指定了检索策略,就会覆盖映射文件中配置的检索策略。在HQL查询语句中显示指定的检索策略包括以下内容。
->left join fetch:覆盖映射文件中配置的检索策略,在程序中显示指定迫切左外连接检索策略。
->inner join fetch:覆盖映射文件中配置的检索策略,在程序中显示指定迫切内连接检索策略。
->QBC通过FetchMode类来显示指定检索策略,FetchMode类有3个静态实例。
FetchMode.DEFAULT:这个是默认值,表示采用映射文件中配置的检索策略。
FetchMode.JOIN:覆盖映射文件中配置的检索策略,在程序中显示指定迫切左外连接检索策略。
fetchMode.SELECT:采用映射文件配置的检索策略,并且通过单独的select语句检索关联的对象。其作用和映射文件中的fetch属性为select相同。
(3)HQL支持各种各样的连接查询,如下:
假定Customer.hbm.xml文件中对orders集合使用延迟检索策略。
下图比较了各种连接方式运行时行为:
如图:
从上图可以看出,尽管迫切左外连接和左外连接对应同样的左外连接SQL查询语句,
但是前者对Cusotmer对象的orders集合采用迫切左外连接检索策略,因此orders集合立即被初始化,而后者orders集合采用映射文件中配置的延迟检索策略,因此orders集合不会被立即初始化。
三:投影查询
投影查询是指查询结果仅包含部分实体或实体的部分属性。投影是通过select关键字来实现的。
下面的HQL查询语句会检索出Customer及关联的Order对象;
如果希望查询结果中只包含Customer对象,可以使用以下形式:
select 关键字还能用于选择对象的部分属性,例如:
以上HQL查询语句对应的SQL语句为:
(1):动态实例查询结果
从上面的例子中看出,当select语句只选择实体的部分属性时,Query接口的list()方法返回的集合存放的是关系数据,集合中的每个元素代码表查询结果的一条记录。可以定义一个CustomerRow类来包装这些记录,使程序代码完全运用面向对象的语义来访问查询结果集。CustomerRow类采用JavaBean的形式,他的属性与select语句中的选择的实体的属性对应。以下是CusotmerRow类的源程序:
以下是CustomerRow类的源程序:
在HQL查询语句中,声明返回CustomerRow的实例:
CustomerRow类不需要是持久化类,因此不必创建它的对象-关系映射文件,它仅用于把select语句查询处理的关系数据包装成Java对象。
(2):过滤查询结果中重复的元素
使用select语句时,返回的查询结果中会包含重复的元素。假如CUSTOMES表存在
name字段相同值的记录,那么以下的Query的list()方法返回的查询结果会包含重复的元素:
以上Query的list()方法返回的查询结果包含
还可以用distinct关键字来保证查询结果不会返回重复的元素。
以上的HQL查询语句会过略掉重复的记录,因此Query的list()返回的集合中不会出现重复的元素。
五:报表查询
报表查询用于对数据分组和统计,与SQL一样,HQL利用select关键字选择需要的查询的数据。用group by关键字对数据分组,用having关键字对分组数据设定约束条件。
完整的HQL语法格式如下,方括号以内的内容为可选项。
1:使用聚集函数
在HQL查询语句中可以调用以下的聚集函数。
-> count() 统计记录条数
-> min() 求最小值
-> max() 求最大值
-> sum() 求和
-> avg() 求平均值
(1):查询customers表中所有记录的条数。
(2):查询CUSTOMERS表中所有客户的平均年龄
(3):查询customers 表中所有客户年龄的最大值和最小值。
(4):统计customers表中所有客户姓名的数目,忽略重复的姓名:
2:分组查询
HQL查询语句中的group by自己用于分组查询,它和SQL中的用法相似。下面举例说明它的用法。
(1):按照姓名分组,统计Customers表中具有相同姓名的记录数目。
以上HQL查询语句对应的SQL语句为:
查询结果为:
图:
Query的list()方法返回的集合中包含4个对象数组类型的元素,每个对象数组对应查询结果中的一条记录。
(2):按照客户组,统计每个客户的订单数目:
以上HQL查询语句对应的SQL语句为:
该查询语句的查询结果如下:
图:
(3):统计每个客户发出的所有的订单的总价:
以上HQL查询语句对应的SQL语句为:
运行结果为:
图:
having子句用于为分组查询结果加上约束,如,以下查询语句仅仅统计具有一条以上 的订单客户的所有订单的总价。
以上的HQL查询语句对应的SQL语句为:
该查询语句的查询结果如下:
图:
3:优化报表查询的性能
select语句仅选择查询持久化类的部分属性事,Hibernate返回的查询结果为关系数据,而非持久化对象。如:
以上两条HQL查询语句对应的SQL语句相同,因此都能查询数据库中相同的数据。
区别在于前者返回的是Customer和Order持久化对象,它们位于session的缓存中,
Session会保证它们的唯一性。后者返回的是关系数据,它们不会占用Session的缓存,
只要应用程序中没有任何变量应用这些数据,它们占用的内存就会被JVM的垃圾回收器回收。
报表查询通常会处理大量的数据,如对于以上查询语句,可能会检索出上万条的CUSTOMERS
和ORDERS记录。另外一方面,报表查询一般只涉及数据的读操作,而不会修改数据。
如果采用第一种形式的HQL语句,会导致大量的Cutomer和Order持久化对象一直处于
Session的缓存中,而且Session还必须负责这些对象与数据库的同步。如果采用第二种形式的HQL语句,能提高报表查询的性能,只要应用程序不再引用这些数据,他们占用的内存就会被释放。
对于第二种形式的HQL语句,可以定义一个JavaBean来包装查询结果中的关系数据,使应用程序仍旧能按照面向对象的方式来访问查询结果,如:
tips:CustomerOrder类不是持久化类,它的实例不会被加入到Session的缓存中。
在HQL查询语句中,如果对Customer类赋予别名’c’,就可以通过c.name的形式访问name属性,还可以通过c.homeAddress.provice的形式访问homeAddress组件的
Province属性。
//HQL检索方式 session. createQuery(“from Customer c where c.homeAddress.province Like ‘%hai’”);
Tips:Customer类的homeAddress(表示家庭地址)属性为Address类型,Customer
类与Address类之间为组成关系。在Customer.hbm.xml文件中,用<component>
元素来映射homeAddress属性。
下面HQL查询语句通过o.customer.name 的形式访问与Order关联的Customer对象的name属性:
List result = session. createQuery(“from Order o where o.customer.name like ’T%’”); .list(); for (Iterator it = result.iterator();it.hasNext();) { Order order = (Order)it.next(); ... }
以上程序代码中的HQL语句尽管没有使用join关键字,其实指明使用内连接查询,它实际对应的SQL语句为:
Select o.id,o.order_number,o.price,o.customer_id From orders o,customers c where o.customer_id = c.id And c.name like ‘T%’;
在select子句中也可以使用隐式内连接,如:
Select distinct o.customer.name from Order o where o.price > 100;
QBC不支持隐式内连接
隐式内连接适合用于多对一和一对一的关联,但适用于一对多或多对多关联。
如:
from Customer c where c.orders.orderNumber like ‘T%’是错的。
假定有3个类,它们的关联关系如下图所示,类A和类B以及类B和类c都是多对一关联关系。
图:
下面四种查询方式返回的查询结果是一样的: //HQL隐式连接 From A a where a.b.c is not null; //HQL显示内连接 Select a from A a inner join a.b b where b.c is not null; //HQL显示内连接 Select a from A a inner join a.b inner join b.c c where c is not null; 对于右连接(略):
二:关联级别运行时的检索策略
(1):如果在HQL或QBC程序代码中没有显示指定检索策略,将使用映射文件中配置的检索策略。但是有个例外,即HQL总是忽略映射文件中设置的迫切左外连接检索策略。也就是说,即使映射文件中设置了迫切左外连接检索策略,如果HQL查询语句没有显示指定这种策略,那么HQL仍然采用延迟检索策略。因此,如果希望HQL采用迫切左外连接检索策略,就必须在HQL查询语句中显示指定它。
(2)如果在HQL或QBC程序代码中显示指定了检索策略,就会覆盖映射文件中配置的检索策略。在HQL查询语句中显示指定的检索策略包括以下内容。
->left join fetch:覆盖映射文件中配置的检索策略,在程序中显示指定迫切左外连接检索策略。
->inner join fetch:覆盖映射文件中配置的检索策略,在程序中显示指定迫切内连接检索策略。
->QBC通过FetchMode类来显示指定检索策略,FetchMode类有3个静态实例。
FetchMode.DEFAULT:这个是默认值,表示采用映射文件中配置的检索策略。
FetchMode.JOIN:覆盖映射文件中配置的检索策略,在程序中显示指定迫切左外连接检索策略。
fetchMode.SELECT:采用映射文件配置的检索策略,并且通过单独的select语句检索关联的对象。其作用和映射文件中的fetch属性为select相同。
(3)HQL支持各种各样的连接查询,如下:
//无连接 from Customer c where c.name like ‘T%’; //迫切左外连接 from Customer c left join fetch c.orders o where c.name like ‘T%’ //左外连接 from Customer c left join c.orders o where c.name like ‘T%’; //迫切内连接 from Customer c inner join fetch c.orders o where c.name like ‘T%’; //内连接 from Customer c inner join c.orders o where c.name like ‘T%’; //右外连接 from Customer c right join c.orders o wehre c.name like ‘T%’
假定Customer.hbm.xml文件中对orders集合使用延迟检索策略。
下图比较了各种连接方式运行时行为:
如图:
从上图可以看出,尽管迫切左外连接和左外连接对应同样的左外连接SQL查询语句,
但是前者对Cusotmer对象的orders集合采用迫切左外连接检索策略,因此orders集合立即被初始化,而后者orders集合采用映射文件中配置的延迟检索策略,因此orders集合不会被立即初始化。
三:投影查询
投影查询是指查询结果仅包含部分实体或实体的部分属性。投影是通过select关键字来实现的。
下面的HQL查询语句会检索出Customer及关联的Order对象;
from Customer c join c.orders o where o.orderNumber like ‘T%’;
如果希望查询结果中只包含Customer对象,可以使用以下形式:
select c from Customer c join c.orders o where o.orderNumber like ‘T%’;
select 关键字还能用于选择对象的部分属性,例如:
Iterator it = session.createQuery(“select c.id,c.name, o.orderNumber from Customer c join c.orders o where o.orderNumber like ‘T%’”).list().iterato(); while (it.hasNext()) { Object[]row = (Object[])it.next(); Long id = (Long)row[0]; String name = (String)row[1]; String orderNumber = (String)row[2]; System.out.println (id+“ ”+name+” ”+orderNumber); }
以上HQL查询语句对应的SQL语句为:
Hibernate: select customer0_.id as col_0_0_, customer0_.name as col_1_0_, orders1_.order_number as col_2_0_ from customers customer0_ inner join orders orders1_ on customer0_.id=orders1_.customer_id where orders1_.order_number like 'T%' 程序打印的结果为: 1 Tom Tom_Order001 1 Tom Tom_Order002 1 Tom Tom_Order003
(1):动态实例查询结果
从上面的例子中看出,当select语句只选择实体的部分属性时,Query接口的list()方法返回的集合存放的是关系数据,集合中的每个元素代码表查询结果的一条记录。可以定义一个CustomerRow类来包装这些记录,使程序代码完全运用面向对象的语义来访问查询结果集。CustomerRow类采用JavaBean的形式,他的属性与select语句中的选择的实体的属性对应。以下是CusotmerRow类的源程序:
以下是CustomerRow类的源程序:
public class CustomerRow implements Serializable{ private Long id; private String name; private String orderNumber; /**必须提供用于初始化所有属性的构造方法**/ public CustomerRow(Long id,String name,String orderNumber) { this.id = id; this.name = name; this.orderNumber = orderNumber; } /**省略set和get方法**/ }
在HQL查询语句中,声明返回CustomerRow的实例:
Iterator it = session.createQuery("select new com.entity.CustomerRow(c.id,c.name,o.orderNumber) from Customer c join c.orders o where o.orderNumber like 'T%'").list().iterator(); while (it.hasNext()) { CustomerRow c = (CustomerRow)it.next(); System.out.println(c.toString()); }
CustomerRow类不需要是持久化类,因此不必创建它的对象-关系映射文件,它仅用于把select语句查询处理的关系数据包装成Java对象。
(2):过滤查询结果中重复的元素
使用select语句时,返回的查询结果中会包含重复的元素。假如CUSTOMES表存在
name字段相同值的记录,那么以下的Query的list()方法返回的查询结果会包含重复的元素:
List result = session.createQuery(“select c.name from Customer c”).list(); Set set = new HashSet(result);//过略重复对象 for(Iterator it = set.iterator();it.hasNext();){ String name = (String)it.next(); System.out.println (name); }
以上Query的list()方法返回的查询结果包含
Hibernate: select customer0_.name as col_0_0_ from customers customer0_ Linda Jack Tom Mike
还可以用distinct关键字来保证查询结果不会返回重复的元素。
Iterator it = session.createQuery(“select distinct c.name from Customer c”).list().iterator(); while (it.hasNext()) { String name = (String)it.next(); System.out.println (name); }
以上的HQL查询语句会过略掉重复的记录,因此Query的list()返回的集合中不会出现重复的元素。
五:报表查询
报表查询用于对数据分组和统计,与SQL一样,HQL利用select关键字选择需要的查询的数据。用group by关键字对数据分组,用having关键字对分组数据设定约束条件。
完整的HQL语法格式如下,方括号以内的内容为可选项。
[select....] from ....[where....][group by... [having...]][order by...]
1:使用聚集函数
在HQL查询语句中可以调用以下的聚集函数。
-> count() 统计记录条数
-> min() 求最小值
-> max() 求最大值
-> sum() 求和
-> avg() 求平均值
(1):查询customers表中所有记录的条数。
Long count=(Long)session.createQuery(“select count(*) from Customer c”).uniqueResult(); 上面的HQL查询语句返回Long类型的查询结果。
(2):查询CUSTOMERS表中所有客户的平均年龄
Double age = (Double)session.createQuery( “select avg(c.age) from Customer c” ).uniqueResult();
(3):查询customers 表中所有客户年龄的最大值和最小值。
Object[]os = (Object[])session.createQuery(“select max(c.age),min(c.age) from Customer c”).uniqueResult(); Integer maxAge = (Integer)os[0]; Integer minAge = (Integer)os[1]; System.out.println (maxAge); System.out.println (minAge);
(4):统计customers表中所有客户姓名的数目,忽略重复的姓名:
Long count = (Long)session.createQuery(“ count(distince c.name) from Customer c ”).uniqueResult();
2:分组查询
HQL查询语句中的group by自己用于分组查询,它和SQL中的用法相似。下面举例说明它的用法。
(1):按照姓名分组,统计Customers表中具有相同姓名的记录数目。
Iterator it = session. createQuery(“select c.name,count(c) from Customer c group by c.name”).list().iterato(); while (it.hasNext()) { Object[] pair = (Object[])it.next(); String name = (String)pair[0]; Long count = (Long)pair[1]; System.out.println (name+” ”+count); }
以上HQL查询语句对应的SQL语句为:
select name ,count(ID) from customers group by name;
查询结果为:
图:
Query的list()方法返回的集合中包含4个对象数组类型的元素,每个对象数组对应查询结果中的一条记录。
(2):按照客户组,统计每个客户的订单数目:
Query query = session.createQuery("select c.id ,c.name,count(o) as orderCount from Customer c left outer join c.orders o group by c.id"); List result = query.list(); for (Iterator it = result.iterator();it.hasNext();) { Object []obj = (Object[])it.next(); Long id = (Long)obj[0]; String name = (String)obj[1]; Long count = (Long)obj[2]; System.out.println(id+" "+name+" "+count); }
以上HQL查询语句对应的SQL语句为:
select c.id,c.name,count(o.id) from Customers c left outer join orders o on c.id = o.customer_id group by c.id;
该查询语句的查询结果如下:
图:
(3):统计每个客户发出的所有的订单的总价:
Query query = session.createQuery("select c.id,c.name , sum (o.price) from Customer c left outer join c.orders o group by c.id"); List result = query.list(); for (Iterator it = result.iterator();it.hasNext();) { Object []obj = (Object[])it.next(); Long id = (Long)obj[0]; String name = (String)obj[1]; Double sum = (Double)obj[2]; System.out.println(id+" "+name+" "+sum); }
以上HQL查询语句对应的SQL语句为:
select c.id,c.name,sum(o.price) from customers c left outer join orders o on c.id = o.customer_id group by c.id;
运行结果为:
图:
having子句用于为分组查询结果加上约束,如,以下查询语句仅仅统计具有一条以上 的订单客户的所有订单的总价。
Query query = session.createQuery("select c.id,c.name , sum (o.price) from Customer c inner join c.orders o group by c.id having count(o) > 1 "); List result = query.list(); for (Iterator it = result.iterator();it.hasNext();) { Object []obj = (Object[])it.next(); Long id = (Long)obj[0]; String name = (String)obj[1]; Double sum = (Double)obj[2]; System.out.println(id+" "+name+" "+sum); }
以上的HQL查询语句对应的SQL语句为:
select c.id,c.name,sum(o.price) from customers c inner join orders o on c.id = o.customer_id group by c.id having (count(o.id) > 1);
该查询语句的查询结果如下:
图:
3:优化报表查询的性能
select语句仅选择查询持久化类的部分属性事,Hibernate返回的查询结果为关系数据,而非持久化对象。如:
->from Customer c inner join c.orders o group by c.age; ->select c.id,c.name,c.age,o.id,o.order_number,o.customer_id from Customer c inner join c.orders c group by c.age;
以上两条HQL查询语句对应的SQL语句相同,因此都能查询数据库中相同的数据。
区别在于前者返回的是Customer和Order持久化对象,它们位于session的缓存中,
Session会保证它们的唯一性。后者返回的是关系数据,它们不会占用Session的缓存,
只要应用程序中没有任何变量应用这些数据,它们占用的内存就会被JVM的垃圾回收器回收。
报表查询通常会处理大量的数据,如对于以上查询语句,可能会检索出上万条的CUSTOMERS
和ORDERS记录。另外一方面,报表查询一般只涉及数据的读操作,而不会修改数据。
如果采用第一种形式的HQL语句,会导致大量的Cutomer和Order持久化对象一直处于
Session的缓存中,而且Session还必须负责这些对象与数据库的同步。如果采用第二种形式的HQL语句,能提高报表查询的性能,只要应用程序不再引用这些数据,他们占用的内存就会被释放。
对于第二种形式的HQL语句,可以定义一个JavaBean来包装查询结果中的关系数据,使应用程序仍旧能按照面向对象的方式来访问查询结果,如:
select new CustomerOrder(c.id,c.name,c.age,o.id,o.order_number, o.customer_id) from Customer c inner join c.orders c group by c.age;
tips:CustomerOrder类不是持久化类,它的实例不会被加入到Session的缓存中。
相关文章推荐
- 北京,北京
- String类的一些内部实现方法
- Calculate CAN bit timing parameters -- STM32
- ADMT3.2域迁移之Server2003至Server2012系列(六)安装SQL Server2008
- 欢迎使用CSDN-markdown编辑器
- 数据结构:define实现链表
- etymology-F
- 1111
- JavaScript之函数作用域
- undefined与null的区别
- Fastjson介绍
- 详解Redis中的双链表结构
- 图片滚动(UP)的JS代码详解(offsetTop、scrollTop、offsetHeigh)【转】
- 基于java/mysql 一些转义字符
- 简析ASP.NET网站的创建与发布过程
- 现代科技的设计方法
- 09-如何快速的向表中插入数据
- 大一学习历程总结
- hdu 1698(区间修改)
- 树莓派自动启动设置