读书人

精通hibernate学习札记(8-2)[检索方

发布时间: 2012-09-22 21:54:54 作者: rapoo

精通hibernate学习笔记(8-2)[检索方式]

4、报表查询

报表查询用于对数据分组和统计,完整的HQL语法:
[select...] from ... [where...] [group by... [having...]] [order by...]

4.1 投影查询

指查询结果仅包含部分实体或实体的部分属性。投影是通过select关键字实现。
from? Customer c join c.orders o where o.orderNumber like 'T%'
//会检索出Customer 及关联的Order对象
select c from Customer c join c.orders o where o.orderNumber like 'T%'
//结果中只包含Customer对象
select c.id,c.name,o.orderNumber from Customer c join c.orders o where ...
//返回的list中,放的是Object[],对应选择的属性集合

4.1.1 动态实例化查询结果

select c.id,c.name,o.orderNumber from Customer c join c.orders o where ...
//返回的list中,放的是Object[],对应选择的属性集合
该查询结果返回的list集合存放的是关系数据,List中的每个元素代表查询结果的一条记录,可以定义一个CustomerRow类来包装这些记录,使程序代码能完全运用面向对象的语义来访问查询结果集。CustomerRow类采用JavaBean的形式,他的属性与Select语句中选择的实体的属性对应。
Iterator it = session.createQuery("select new mypack.CustomerRow(c.id,c.name,c.orderNumber) from Customer c join c.orders o where o.orderNumber like 'T%'").list().iterator();
while(it.hasNext()){
????? CustomerRow row = (CustomerRow)it.next();
}

4.1.2 过滤查询结果中的重复元素

select c.name from Customer c
返回的结果可能会有重复的名字,可以使用HashSet顾虑结果,或使用distinct关键字
select distinct c.name from Customer c

4.2 使用聚集函数

HQL查询语句中可调用的聚集函数:
a. count():统计记录条数
Integer count = (Integer)session.createQuery("select count(*) from Customer c").uniqueResult();
Integer count = (Integer)session.createQuery("select count(distinct c.name) from Customer c").uniqueResult();
b.min():求最小值
c.max():求最大值
Object [] os = (Object[])session.createQuery("select max(c.age),min(c.age) from Customer c").uniqueResult();
d.sum():求和
e.avg():求平均值
Float age = (Float)session.createQuery("select avg(c.age) from Customer c ").uniqueResult();

4.3 分组查询

(1)按照姓名分组,统计具有相同姓名的记录数目:
session.createQuery("select c.name,count(c) from Customer c group by c.name")
......

4.4 优化报表查询的性能

当select语句仅仅选择查询持久化类的部分属性时,Hibernate返回的查询结果为关系数据,而非持久化对象。eg:

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能查询处相同的数据,不同的是,前者返回的是Customer和Order持久化对象,他们位于Session的缓存中;后者返回的是关系数据,不会占用session的缓存,可以被JVM回收。
????? 报表查询一般会处理大量数据,如果使用第一种方式会导致大量Customer和Order持久化对象一直位于Session中,session还必须负责这些对象的同步,会降低性能!

5、高级查询技巧

5.1 动态查询

如果在程序运行前就明确了查询语句的内容(也称为静态查询),应该考虑HQL查询方式;但是如果只有在运行时才能明确查询语句的内容(也称为动态查询)QBC比HQL更加方便。

5.2 集合过滤

假设Customer的orders集合使用延迟策略,当调用customer.getOrders().iterator()方法,就会初始化orders集合,会加载所有与Customer关联的Order持久化对象。这种方式有两大不足:
a. 如果有1000个Order对象与Customer关联,就会加载1000个Order对象,而这些并不一定是都需要的,会因为加载多余的Order对象而影响性能。(如:访问价格大于100的Order对象)
b. 不能对orders集合排序

解决方法:
a. 通过HQL或QBC查询
session.createQuery("from Order o where o.customer= :customer and o.price>100 order by o.price");
b. 使用集合过滤
Query query = session.createFilter(customer.getOrders(),"where this.price>100 order by this.price")

session.createFilter()方法与你过来过虑集合,具有以下特点:
a. 返回Query类型的实例
b. 第一个参数指定一个持久化对象的集合,这个集合是否已经被初始化并没有关系,但是它所属的对象必须出于持久化状态
c. 第二个参数指定过滤条件,由HQL查询语句组成
d.不管持久化对象的集合是否已经被初始化,Query的list()方法都会进行查询,以上代码对应的SQL:
select ID,ORDER_NUMBER,PRICE from ORDERS where CUSTOMER_ID=1 and PRICE>100 order by PRICE;
e.如果Customer对象的orders集合已经被初始化,为了保证session的缓存中不会出现OID相同的Order对象,list()方法不会在创建Order对象,仅仅返回session中的引用。
f. 如果Customer对象的orders集合还没有被初始化,Query的list方法会创建相应的order对象,但是不会初始化Customer对象的orders集合。

集合过滤出了用于为集合排序或设置约束条件,还有其他用途:
(1)为Customer对象的orders集合分页
List result = session.createFilter(customer.getOrders(),"order by this.price asc")
????????????????????? .setFirstResult(10)
????????????????????? .setMaxResults(50)
?????????????????????? .list();
(2)检索Customer对象的orders集合中Order对象的订单编号
List result = session.createFilter(customer.getOrders(),"selct this.orderNumber").list();
(3)检索数据库中与Customer对象的orders集合中Order对象的价格相同的所有Order对象:
List result = session.createFilter(customer.getOrders(),"select other from Order other where other.price = this.price").list();
(4)检索Order对象的lineItems集合中LineItem对象的Item
List result = session.createFilter(order.getLineItems(),"select this.item").list();

5.3 子查询

from Customer c where 1<(select count(o) from c.orders o )
子查询必须放在括号内。

(1)子查询可以分为相关子查询和无关子查询。相关子查询指子查询语句引用了外层查询语句定义的别名,而无关子查询指子查询语句与外层查询语句无关。
(2)HQL子查询依赖于底层数据库对子查询的支持能力。MySQL从4.1.x才开始支持子查询。如果希望应用程序能进行移植,应避免使用HQL的子查询功能。无关子查询语句可以改写为单独的查询语句,相关子查询语句可以改写为连接查询和分组查询。eg:
select c from Customer c join c.orders o group by c.id having count(o)>1
(3)如果子查询返回多条记录,可以用以下关键字来量化。
--all: 表示子查询返回的所有记录
--any: 表示子查询语句返回的任意一条记录
--some: 与'any'等价
--in: 与'any'等价
--exists: 表示子查询语句至少返回一条记录。
(4)如果子查询语句查询的是集合,HQL提供了缩写语法,eg:
Iterator it = session.createQuery("from Customer c where精通hibernate学习札记(8-2)[检索方式]rder in elements(c.orders)")
??????????????????? .setEntity("order",order)
     .list()
??????????????????? .iterator();
以上elements()函数等价于一个子查询语句:
from Customer c where精通hibernate学习札记(8-2)[检索方式]rder in (from c.orders)

HQL提供了一组操作集合的函数或者属性
--size函数或size属性:获得集合中元素的数目
--minIndex函数或属性:对于建立了索引的集合,获得最小的索引
--maxIndex函数或属性:对于建立了索引的集合,获得最大的索引
--minElement函数或属性:对于包含基本类型元素的集合,获得集合中取值最小的元素
--maxElement函数或属性:对于包含基本类型元素的集合,获得集合中取值最大的元素
--elements函数:获得集合中所有的元素
from Customer c where c.orders.size>0 or
from Customer c where size(c.orders)>0

5.4 本地SQL查询

为了把SQL查询返回的关系数据映射为对象,需要在SQL查询语句中为字段指定别名。
String sql = "select cs.ID as {c.id},cs.NAME as {c.name},cs.AGE as {c.age} from CUSTOMERS cs where cs.ID=1";
Query query = session.createSQLQuery(sql,"c",Customer.class);
// session.createSQLQuery(sql,"类别名",Customer.class);类的别名必须位于大括号里。

String sql = "select {c.*},{o,*} from CUSTOMERS c inner join ORDERS o where c.ID=o.CUSTOMER_ID";
Query query = session.createSQLQuery(sql,new String[]{"c","o"},new Class[]{Customer.class,Order.class});

SQL放入映射文件中:
<sql-query name="findCustomersAndOrders"><![CDATA[
??????????? select {c.*},{o,*} from CUSTOMERS c inner join ORDERS o where c.ID=o.CUSTOMER_ID]]>
????????? <return alias="c" alt="精通hibernate学习札记(8-2)[检索方式]" height="595" src="/img/2012/09/14/1451472655.jpg">

?

读书人网 >软件架构设计

热点推荐