您的位置:首页 > 其它

Hibernate检索方式(二)

2015-08-11 12:23 316 查看
一:隐式内内连接

在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的缓存中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: