zoukankan      html  css  js  c++  java
  • Hibernate的查询语言之HQL(二)——Hibernate查询的from字句

      from 是最简单的HQL语句,也是最基本的HQL语句。from 关键字后紧跟持久化类的类名。例如:

    1 from Person

      表明从Person持久化类中取出全部的实例。

      大部分时候,推荐位该Person的每个实例取一个别名。例如:

    1 from Person as p

      上面的as是可选的,但为了增加可读性,建议保留。

      from 后面还可以同时出现多个持久化类,此时将产生一个笛卡尔积或跨表连接,但实际上这种用法很少使用,因为通常我们可能需要使用跨表连接时,可以考虑使用隐士连接或者显示连接,而不是直接在from后紧跟多个表名。

      

      关联和连接

      当程序需要从多个数据表中取得数据时,SQL语句将会考虑使用多表连接查询。Hibernate使用关联映射来处理底层数据表之间的连接,一旦我们提供正确的映射后,当程序通过Hibernate进行持久化访问时,将可利用Hibernate的关联进行连接。

      HQL支持两种关联连接(join)形式: 隐式(implicit)显式(explicit)

      隐式连接形式不适用 join 关键字,使用英文点号(.)来隐式连接关联实体,而Hibernate底层将自动进行关联查询。例如如下HQL语句

    1 //查询Person持久化实体
    2 from Person as p 
    3 where p.myEvents.titile > :title

      上面的p.myEvents属性的实质是一个持久化实体,因此Hibernate底层隐式自动进行连接查询。

      

      显式连接则需要使用 xxx join 关键字。例如下面的语句:

    1 from Person p
    2 inner join p.myEvents event
    3 where event.happenDate < :endDate

      使用显式连接 时可以为相关联的实体,甚至是关联集合中的全部元素指定一个别名。

      Hibernate支持的HQL连接直接借鉴了 SQL99 多表查询的关键字,可使用如下几种连接方法。

      》inner join (内连接),可简写成 join。

      》left outer join(左外连接),可简写成 left join。

      》right outer join(右外连接),可简写成right join。

      》full join(全连接),并不常用。

      使用显式连接时,还可通过HQL的with关键字来提供额外的连接条件,例如如下HQL语句:

    1 from Person p
    2 inner join p.myEvents event
    3 with p.id > event.id
    4 where event.happenDate < :endDate

      Hibernate会将这种显式连接转换成SQL99多表连接的语法,所以HQL语句中with关键字的作用等同于SQL99中 on 关键字的作用;都是用于指定连接条件。通过在HQL语句中使用with关键字,可以让HQL语句执行非等值连接查询。

      

      不难发现 要想完全掌握HQL,不懂SQL是完全不行的。

      还有一点必须指出:由于此处的inner join, left join, right join, full join的实质依然是基于底层的SQL的内,左,外连接的,所以如果底层SQL不支持这些外连接,那么执行相应的HQL时就会相应的引发异常。

      对于隐式连接和显式连接还有如下两点区别:

      》隐式连接底层将转换为SQL99 的交叉连接,显式连接底层将转换成SQL99 的 inner join, left join, right join等连接。

      对于from Person p where p.myEvent.title > :title这条隐式连接的HQL语句,执行HQL查询后将看到产生如下所示的SQL语句。

     1 select 
     2     person0_.person_id as person1_0_,
     3     person0_.name as name_0,
     4     person0_.age as age0_,
     5     person0_.event_id as event4_0_
     6 from
     7     person person0_ cross
     8     join
     9     event myevent1_
    10 where
    11     person0_.event_id = myevent1_.event_id
    12     and myevent1_.title > ?

      而对于from Person p left join p.myEvents event where event.happenDate < :endDate 这条显式连接的HQL语句,执行HQL查询后将看到产生如下所示的SQL语句:

     1 select 
     2     person0_.person_id as person1_0_,
     3     person0_.name as name0_,
     4     person0_.age as age0_,
     5     person0_.event_id as event4_0_
     6 from person person0_
     7 left outer join
     8         event myevent1_
     9             on person0_.event_id = myevent1_.event_id
    10 where
    11     myevent1_.happenDate < ?

      对比这两条SQL语句,不难发现第一条SQL语句是SQL99的交叉连接,而第二条SQL语句则是SQL99的左外连接语法——具体到底使用哪种连接方法,取决于HQL语句的显示连接使用了哪种连接方式。

      》隐式连接和显示连接查询后返回的结果不同。

      当HQL语句中省略select关键字时,使用隐式连接查询返回的结果是多个被查询实体组成的集合。如上面第一条SQL语句所示,它只选择person表中的数据列,所以查询得到的结果是Person对象组成的集合

      当使用显式连接查询的HQL语句中省略select关键字时,返回的结果也是集合,但集合元素是被查询持久化对象,所有被关联的持久化对象所组成的数组。如上面的第二条SQL语句所示,他同时选择了person,event表中的所有数据列,查询得到的结果集的每条记录即包含了Person实体的全部属性,也包含了MyEvent实体的全部属性。Hibernate会把每条记录封装成一个集合元素,用属于Person的属性创建Person对象,属于MyEvent的属性创建MyEvent对象.....多个持久化实体最后封装成一个数组来作为集合元素。

      如:

     1         Session session = HibernateSessionFactory.getSession();
     2         Transaction tx = session.beginTransaction();
     3         String hql = "from Person p join p.myEvents event";
     4         List<Object[]> datas = session.createQuery(hql).list();
     5         for(Object[] data:datas){
     6             System.out.println(data[0]);//输出Person持久化实体对象
     7             System.out.println(data[1]);//输出MyEvent持久化实体对象
     8         }
     9         tx.commit();
    10         session.close();

      关于隐式连接和显示连接还有非常主要的一点需要指出,这是由Hibernate版本升级所引发的问题。在Hibernate3.2.2之前的版本, Hibernate会对所有关联实体自动使用隐式连接。对于如下HQL语句:

     

    1 from Person p
    2 where p.myEvents.title = :eventTitle

      无论如何, Hibernate将对上面的p.myEvents.title自动使用隐式连接,因此上面的HQL语句总是有效的。  

      从Hibernate3.2.3以后, Hibernate改变了这种隐式连接的策略,还是对于这条同样的HQL语句,则可能出现以后几种情况:

      》如果myEvents 是普通组件属性,或单个的关联实体,则Hibernate会自动生成隐式内连接,上面的HQL语句依然有效。

      》如果myEvents是一个集合(包括1-N,N-N关联),那么系统将出现:QueryException异常,异常提示信息为:

    1 org.hibernate.QueryException: illegal attempt to dereference collection

      根据Hibernate的官方说法:这样可以使的隐式连接更具确定性(原文:This makes implicit joins more deterministic)

      为此, Hibernate推荐上面的HQL语句写成:

    1 from Person p
    2 inner join p.myEvents e
    3 where e.title = :eventTitle

      这条HQL语句将会返回一个集合,集合元素是Person实体和MyEvent实体组成的数组。

      如果只想获取Person组成的集合,则需要改写成:

    1 select p
    2 from Person p
    3 join p.myEvents e
    4 where e.title = :eventTitle

      但上面的语句可能返回多个完全相同的Person对象,想一想SQL多表连接查询的结果就可知道原因了。

      如果想得到由Person实体组成的集合,且元素不重复,应该改为如下SQL语句:

    1 select distinct p 
    2 from Person p
    3 inner join p.myEvents e
    4 where e.title =  :eventTitle

      也就是说,对于Hibernate 3.2.3以后的版本,如果关联实体是单个实例或单个的组件属性,HQL依然可以可以使用英文句点(.)来隐式连接关联实体或组件;但如果关联实体是集合(包括1-N关联,N-N关联和集合元素是组件等),则必须使用 xxx join 来显式连接关联实体或组件。

      对于有集合属性的,Hibernate默认采用延迟加载策略。如果Session被关闭,Person实例将无法访问关联的scores属性。

      为了解决该问题,可以在Hibernate映射文件中指定 lazy = "false" 来关闭延迟加载。

      还有一种方法,使用join fetch,例如:

    1 from Person p
    2 join fetch p.scores

      上面的关键字将导致Hibernate在初始化Person对象时,同时抓取该Person关联的scores集合属性。

      使用join fetch时通常无需指定别名,因为相关联的的对象不应在where字句(或其他任何字句)中使用。而且被关联的对象也不会再被查询的结果中直接返回,而是应该通过其父对象来访问。

      使用fetch关键字时有如下几个注意点

      》fetch不应该与setMaxResult()或setFirstResult()公用。因为这些操作都是基于结果集的,而在预先抓取集合类时可能包含重复的数据,即无法预先知道精确的行数。

      》fetch不能独立与with条件一起使用。

      》如果在一次查询中fetch多个集合,可以查询返回笛卡尔积,因此请多加注意。

      》对bag映射而言,同时join fetch多个集合可能出现非预期结果,因此需要谨慎使用。

      》full join fetch 与 right join fetch是没有任何意义的。

      如果在映射文件映射属性时同时指定 lazy =  "true" 启动了延迟加载(这种延迟加载是通过字节码增强来实现的),然后程序里有希望预加载那些原本应延迟加载的属性,则可以通过 fetch all properties 来强制Hibernate立即抓取这些属性。例如:

      

    1 from Document fetch all properties order by name
    2 from Document doc fetch all properties where lower(doc.name) like '%cats%'
  • 相关阅读:
    perl 判断网站内容是否变更
    鸿雁电器oa系统中决策支持模块效果
    爬虫1-15
    自动封杀脚本
    图表中如何实现动态变更分类轴与系列值
    错误代码: 1305 PROCEDURE world.insert_data does not exist
    错误代码: 1054 Unknown column 't.createUsrId' in 'group statement'
    错误代码: 1247 Reference 'startTime' not supported (forward reference in item list)
    Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Query was empty
    echarts中地图提示"TypeError:i is undefined"
  • 原文地址:https://www.cnblogs.com/ArtsCrafts/p/Hibernate_HQL2.html
Copyright © 2011-2022 走看看