攻城狮在路上(壹) Hibernate(十四)--- Hibernate的检索方式(下)

本节介绍HQL和QBC的高级用法:各种连接查询、投影查询、报表查询、动态查询、集合过滤和子查询等。另外将归纳优化查询程序代码,从而提高查询性能的各种技巧。
一、连接查询
  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.JOIN
右外连接 right outer join 或者 right join 不支持
交叉连接 Class A, Class B 不支持 适用于不存在关联关系的持久化类


  1、默认情况下关联级别的运行时检索策略
    可以在映射文件中设置关联级别的立即检索、延迟检索或迫切左外连接检索。注意Query API会忽略迫切左外连接的配置。
    若程序代码中没有显式指定与Customer类关联的Order对象的检索策略,那么将采用Customer.hbm.xml映射文件对orders集合设置的检索策略。
    有个例外:HQL会忽略映射文件设置的迫切左外连接检索策略,详见下表。
    HQL和QBC运行时对Customer类的orders集合使用的检索策略:

Customer.hbm.xml文件对orders集合设置的检索策略 HQL QBC
立即检索 立即检索 立即检索
延迟检索 延迟检索 延迟检索
迫切左外连接检索 立即检索 迫切左外连接检索


  2、迫切左外连接
    若程序中显式指定了与Customer关联的Order对象的检索策略,则会覆盖Customer.hbm.xml中的设置。
    实例代码:

//HQL检索方式
List result = session.createQuery("from Customer c left join fetch c.orders o where c.name like 'T%'").list();
//QBC检索方式:
list result = session.createCriteria(Customer.class)
            .setFetchMode("orders", FetchMode.JOIN)
            .add(Restrictions.like("name", "T", MatchMode.START)).list();

for(Iterator it = result.iterator(); it.hasNest()) {
  Customer customer = (Customer)it.next();
}

   HQL中,left join fetch关键字表示迫切左外连接检索策略。在QBC中,FetchMode.JOIN表示迫切左外连接检索策略。
    使用Query或则Criteria的list()方法返回的集合中存放Customer对象的引用,每个Customer对象的orders集合都被初始化,存放所有管理的Order对象。
    注意:当使用迫切左外连接检索策略时,查询结果中可能会包含重复元素,可以通过一个HashSet来过滤重复元素
  3、左外连接
  实例代码:

//HQL检索方式
//如果希望list()返回的集合中仅包含Customer对象,在HQL语句中使用select关键字即可。
List result = session.createQuery("from Customer c left join c.orders o where c.name like 'T%'").list();
for(Iterator paris = result.iteraor(); pairs.hasNext() {
Object[] pair = (Object[])pairs.next();
Customer customer = (Customer)pair[0];
Order order = (Order)pair[1];
//如果orders集合使用延迟加载策略,以下代码会初始化Customer对象的orders集合
customer.getOrders().iterator();
}
View Code

  4、内连接
  在HQL中,使用inner join关键字表示内连接。另外QBC也支持内连接(createAlias("orders", "o"))。
  实例代码:

List result = session.createQuery("from Customer c inner join c.orders o where c.name like 'T%'").list();
for(Iterator paris = result.iteraor(); pairs.hasNext() {
Object[] pair = (Object[])pairs.next();
Customer customer = (Customer)pair[0];
Order order = (Order)pair[1];
//如果orders集合使用延迟加载策略,以下代码会初始化Customer对象的orders集合
customer.getOrders().iterator();
}
View Code

    QBC方式默认仅检索出Customer对象,若要希望QBC返回的集合也包含成对的Customer和Order对象,可以调用Critiria的setResultTransformer()方法。
  5、迫切内连接
    通过inner join fetch关键字来指定,此种方式会覆盖映射文件中指定的检索策略。
    QBC不支持迫切内连接。
  6、内连接
    HQL支持隐式内连接,QBC不支持。
  7、右外连接
    在HQL中,使用right join关键字表示右外连接。
  8、使用SQL风格的交叉连接盒隐式内连接
    此种情况多用于不存在关联关系的两个类:

from Customer, Student

  9、关联级别运行时的检索策略
    该节时对关联级别运行时的检索策略的总结:
    A、如何在HQL或QBC程序代码中没有显示的指定检索策略,将使用映射文件配置的检索策略,例外情况是:HQL总是忽略配置文件中设置的迫切左外连接检索策略。
    B、如果在HQL或QBC程序代码中显示指定了检索策略,就会覆盖映射文件配置的检索策略。
    C、HQL支持各种连接查询:left join fetch\left join\inner join fetch\inner join\right join
    HQL在各种连接方式下的运行时行为:

 

连接方式 对应的SQL查询语句 orders集合的检索策略 查询结果集中的内容
无连接 查询单个CUSTOMERS表 延迟检索策略 集合中包含Customer类型的元素;
集合中无重复元素;
Customer对象的orders集合没有被初始化。
迫切左外连接 左外连接查询CUSTOMERS和ORDERS表 迫切左外连接检索策略 集合中包含Customer类型的元素;
集合中可能有重复元素;
Customer对象的orders集合被初始化。
左外连接 左外连接查询CUSTOMERS和ORDERS表 延迟检索策略 集合中包含对象数组类型的元素,每个对象数组包含一对Customer和Order对象,不同的对象数组可能重复引用同一个Customer对象;
Customer对象的orders集合没有被初始化。
内连接 内连接查询CUSTOMERS和ORDERS表 延迟检索策略 同上
迫切内连接 内连接查询CUSTOMERS和ORDERS表 迫切内连接检索策略 集合中包含Customer类型的元素;
集合中可能有重复元素;
Customer对象的orders集合被初始化。
右外连接 右外连接查询CUSTOMERS和ORDERS表 延迟检索策略 同左外连接

二、投影查询
  投影查询是指查询结果中仅包含部分实体或实体的部分属性。投影式通过select关键字实现的。
  实例代码:

//查询结果仅包含Customer对象
select c from Customer c join c.orders o where o.orderNumber like 'T%'
//仅选择对象的部分属性
select c.id,c.name,c.orderNumber from Customer c;
View Code

  1、动态实例化查询结果
    上面的示例中list()返回的集合存放的是关系数据,而不是对象,可以定义一个类来包装这些属性:

selct new xxx.CustomerBean(c.id, c,name, o.orderNumber) from Customer c join c.orders o where o.orderNumber like 'T%'

  2、过滤查询结果中的重复元素
    前面讲过使用Set来过滤重复元素的方式,这里提供例外一种方式:

select distint c.name from Customer c  

三、报表查询
  报表查询用于对数据分组和统计,用select关键字选择需要查询的数据,使用group by关键字对数据分组,使用having关键字对分组数据设定约束条件。
  HQL语法格式:只有from关键字是必需的。

[select] from ... [where] [group by ... [having...]] [order by...]

  1、使用聚集函数:count()\min()\max()\sum()\avg()\
  2、分组查询:类似于SQL语句。
  3、优化报表查询的性能:主要是考虑查询的报表数据没有必要存放于Session的缓存中。实现方式通过上面获取关系数据的方式即可。

四、高级查询技巧
  概述

  动态查询:在程序运行时动态的决定查询语句的内容。主要通过QBC或QBE实现。
  集合过滤:对集合进行过滤,实现对集合的排序、设置约束条件和分页查询等。
  子查询:在HQL查询语句中嵌入子查询语句。
  本地SQL语句:用本地数据库的SQL方言来查询数据。
  查询结果转换器:对查询结果进行过滤或重组,指定查询结果的内容。

  1、动态查询
    如果在程序运行前就明确查询语句的内容,优先使用HQL查询方式;若在程序运行时才能明确查询语句的内容时,QBC更加方便。
    主要方式是在程序内通过条件判断来决定添加何种查询条件。
  2、集合过滤
    通过session.createFilter(customer.getOrders(),"where ...");
    第一个参数指定一个持久化对象的集合
    第二个参数指定过滤条件,它由合法的HQL查询语句组成。
  3、子查询
    HQL支持在where子句中嵌入子查询语句。要求底层数据库支持子查询,例如MySQL从4.1.x才开始支持子查询。
    可以使用all\any\some\in\exists关键字。
    另外HQL提供了一组操纵集合的函数或者属性:

size()函数或者size属性:获得集合中元素的数目。
minIndex()函数或者minIndex属性:对于建立了索引的集合,获取最小的索引。
maxIndex()函数或者maxIndex属性:对于建立了索引的集合,获取最大的索引。
minElement():对包含基本类型元素的集合,获得集合中取值最小的元素。
maxElement():对包含基本类型元素的集合,获得集合中取值最大的元素。
elements():获得集合中的所有元素。

  4、本地SQL查询
    session.createSQLQuery("select * from CUSTOMERS");
    若要明确指定每一列的Java数据类型:
    addScalar("ID", Hibernate.LONG);
    addEntity(Cusmomer.class);把结果集中的关系数据映射为对象。
    addJoin():用于连接查询。
    上述连接查询方式中,如果在程序代码中直接写SQL语句,会增加维护代码的难度,不建议此种硬编码的方式,替代方案是使用下面的方式:

<sql-query name="findOrderByName">
  <return alias="o" class="mypack.Order"/>
  <return-join alias="c" property="o.customer"/>
  select {o.*},{c.*} from ORDERS o join CUSTOMERS c on o.CUSTOMER_ID=c.ID where c.NAME=:name
</sql-query>

Query query = session.getNamedQuery("findOrderByName").setParameter("name","TOM");
List list = query.list();

  5、查询结果转换器
  在Criteria中,通过Criteria.setResultTransformer()来指定常量:
    Criteria.ROOT_ENTITY:默认值。查询结果中仅包含根节点的实例,可能有重复值。
    Criteria.DISTINCT_ROOT_ENTITY:查询结果中仅包含根节点的实例,没有重复值。
    Criteria.ALIAS_TO_ENTITY_MAP:查询结果中包含根节点的实例及被关联的实例,存放在Map中。
  在HQL检索方式和本地SQL检索方式中,通过setResultTransformer(Transformes.aliasToBean(CustomerDTO.class));
五、查询性能优化
  1、Hibernate优化查询的手段
    A、减低访问数据库的频率,减少select语句的数量。实现手段包括:
      1、使用迫切左外连接或者迫切内连接检索策略。
      2、对延迟加载或立即检索策略设置批量检索数目。
      3、使用查询缓存。
    B、避免多余加载程序中不需要访问的数据:实现手段包括:
      1、使用延迟加载策略。
      2、使用集合过滤。
    C、避免报表查询数据占用缓存。实现手段为利用投影查询功能,查询出实体的部分属性。
    D、减少select语句中字段的数量,从而减低访问数据库的数据量。

  2、iterate()方法
    在启用二级缓存的情况下,可以轻微的提高查询性能。
  3、缓存
    如果查询结果中包含实体,查询缓存只会存放实体的OID;对于投影查询,查询缓存会存放所有的数据值。
    1、适用场合:
      A、在应用程序运行时经常使用的查询语句。
      B、很少对查询语句检索到的数据进行插入、删除或更新操作。
    2、启用查询缓存的步骤:
      A、配置第二级缓存。(详见后续的说明)
      B、配置查询缓存属性:hibernate.cache.user_query_chche=true
      C、对于希望启用查询缓存的查询语句,调用Query接口的setCacheable(true);
      D、若希望更加精粒度的控制查询缓存,可以设置缓存区域。详见后续笔记说明。

六、总结
  比较HQL与QBC的优缺点:

比较方面 HQL检索方式 QBC检索方式
可读性 优点:和SQL语言比较接近,比较容易读懂 缺点:QBC把查询语句肢解为一组Criteria实例,可读性差
功能 优点:功能最强大,支持各种连接查询 缺点:没有HQL的强大,例如不支持报表查询和子查询,而且对连接查询也做了很多限制
查询语句形式 缺点:应用程序必须提供基于字符串形式的HQL查询语句 优点:QBC检索方式封装了基于字符串形式的查询语句,提供了更加面向对象的接口。
何时被解析 缺点:HQL查询语句只有在运行时才会被解析 优点:QBC在编译时就能被解析,因此更加容易排错。
可扩展性 缺点:不具有可扩展性 优点:允许用户扩展Criteria接口
对动态查询语句的支持 缺点:尽管支持生成动态查询语句,但是编程很麻烦 优点:适合于生成动态查询语句
posted @ 2014-06-14 18:32  于辰  阅读(343)  评论(0编辑  收藏  举报