HQL语言入门

SQL和HQL只有一个字母只差,一个是结构化查询语言,一个是Hibernate查询语言,HQL延用了SQL中的语法、关键词。

HQL是Hibernate Query Language的简称,也就是说HQL是一种Hibernate查询语言,所以它在Hibernate世界里应用很方便。HQL是完全面向对象的查询语句,查询功能非常强大,具备多态、关联等特性。链接一篇关于HQL详细介绍的文章《HQL: Hibernate查询语言》,里面讲的内容很详细!

HQL简单介绍


  HQL查询依赖于Query类,每个Query实例对应一个查询对象。使用HQL查询可按如下步骤进行:
        1)获取Hibernate Session对象;
        2)编写HQL语句;
        3)以HQL语句作为参数,调用Session的createQuery方法创建查询对象;
        4)如果HQL语句包含参数,调用Query的setXxx方法为参数赋值;
        5)调用Query对象的list等方法遍历查询结果。



进入HQL世界


  一个ORM框架是建立在面向对象的基础上的。最好的例子是Hibernate如何提供类SQL查询。虽然HQL的语法类似于SQL,但实际上它的查询目标是对象。HQL拥有面向对象语言的所有的特性,这其中包括多态、继承和组合。这就相当于一个面向对象的SQL,为了提供更强大的功能,HQL还提供了很多的查询函数。这些函数可以被分为四类:

  1. 投影函数

  2. 约束函数

  3. 聚合函数

  4. 分组函数

  使用HQL可以建立简单的查询,也可以建立更复杂的查询。在本文中并不讨论那些非常复杂的查询,如含有子查询和很多连接的查询。本文只讨论连接两个表的查询。现在让我们开始接近HQL吧!

   投影

  如谓投影,就是一个可以访问的对象或对象的属性。在HQL中,可以使用from和select子句来完成这个工作。

  from子句返回指定的类的所有实例。如from Order将返回Order类的所有实例。换句话说,以上的查询相当于以下的SQL语句:

select * from order

  from 是最简单的查询子句。from后面可以跟一个或多个类名(类名也可以带有别名)。为了得到Order和Product的所有实例,可以使用如下的查询:

from Order, Product

  和类名一样,别名也可以在from后使用,如下代码如示:

from Order as o, Product p

  当查询很复杂时,加入别名可以减少语句的长度。我们可以看看如下的SQL语句:

select o.*, p.* from order o, product p where o.order_id = p.order_id

  我们可以很容易看出,上面的查询是一对多的关系。在HQL中相当于一个类中包含多个其它类的实例。因此,以上的SQL写成HQL就是:

from Order as o inner join o.products as product

  现在让我们考虑另外一个从表中得到指定属性的情况。这就是最常用的select子句。这在HQL中的工作方式和SQL中一样。而在HQL中,如果只是想得到类的属性的话,select语句是最后的选择。以上的SQL可以使用select子句改成如下的HQL语句:

select product from Order as o inner join o.products as product

  以上的HQL语句将返回Order中的所有Products实例。如果要得到对象的某一个属性,可以将HQL语句写成如下的形式:

select product.name from Order as o inner join o.products as product

  如果要得到多个对象的属性,可以将HQL语句写成如下形式:

select o.id, product.name from Order as o inner join o.products as product


  接下来,我们将进入下一个议题。假设我们需要根据某些条件得到数据。那么以上所述的HQL语句将无法满足需求。为了达到这一目的,我们就要用到下面将要讨论的约束子句。 

约束

  从以上可知,投影返回的是所有的数据。但在大多数时候我们并不需要这么多数据。这就需要对数据进行过滤。在HQL中过滤数据的子句和SQL一样,也是where。它的语法类似于SQL,通过where子句,可以对行进行过滤。我们可以看看下面的SQL语句:

select * from orders where id = ‘1234’

  这条查询语句返回了id等于1234的所有的字段。和这条SQL对等的是下面的HQL语句:

select o from Order o where o.id=’1234’

  从以上两条语句可以看出,它们的where子句非常相似。而它们唯一的不同是SQL操作的是记录,而HQL操作的是对象。在HQL中,除了where子句可以过滤数据外,having子句也可以做到这一点(关于having子句的详细内容我将在分组部分讨论)。投影和约束是两个基本的操作,这两个操作再加上聚合函数的话,那HQL将变得更加强大。下面我们就来讨论什么是聚合。

   聚合

  上述的查询都是将每一个记录(对象)当做一个单位,而如果使用聚合,可以将一类记录(对象)当做一个单位。然后再对每一类的记录(对象)进行一系列地操作,如对某一列取平均值、求和、统计行数等等。HQL支持以下的聚合函数:

  1. avg(…), sum(…)
  2. min(…), max(…)
  3. count(*), count(…), count(distinct…), count(all…)

  以上的聚合函数都返回数值类型。这些操作都可以在select子句中使用,如下所示:

select max(o.priceTotal) + max(p.price) from Order o join o.products p group by o.id

  以上的HQL语句返回了两个值的和:orders表中的priceTotal的最大值和products表中的price的最大值之和。我们还可以使用having子句对分组进行过滤。如我们想按id统计priceTotal小于1000的数量可按如下的HQL语句去实现:

select count(o) from Order o having o.priceTotal < 1000 group by o.id

  我们还可以将聚合函数和having子句一起使用。如我们要按products表的id统计price小于amount的平均数的产品数量,HQL语句如下:

select count(p) from Product p having p.price < avg(amount) group by p.id

  从上面的一系列的HQL语句可以看出,所有通过SQL实现的,都可以通过HQL来实现。

   分组

  在上一部分,已经涉及到了分组的概念。分组操作的是行的集合。它根据某一列(属性)对记录集进行分组。这一切是通过group子句实现的。如下的例子描述了group子句的一般用法。

select count(o) from Order o having o.priceTotal >= 1200 and o.priceTotal <= 3200 group by o.id

  HQL中的分组和SQL中的分组类似。总之,除了一些对SQL的特殊扩展外,其它所有的SQL功能都可以使用HQL描述。在接下来的部分,让我们举例说明如何在java中使用HQL。

在java中使用HQL

  到现在为止,我们已经学习了HQL的基本用法。接下来我们举一个例子来说明如何在Java中使用HQL。下面的例子只给出了主要的部分,由于本文只是讨论HQL的用法,因此,关于Hibernate的一些设置和在main()函数中调用Hibernate的部分并未给出,读者可以参考相关的文当。现在让我们看看下面的例子。

  下面是必须引用的包
 

import java.util.List;
import org.hibernate.*;
import org.hibernate.cfg.*
import com.Order;

  下面是类的声明

public class MyOrder
{
… …
}

  下面让我们来实现MyOrder类的构造函数

public class MyOrder
{
SessionFactory sf;

public MyOrder()
{
Configuration cfg = new Configuration().addClass(Order.class);
sf = cfg.buildSessionFactory();
}
… …
}

  下面的getOrder函数根据priceTotal的区间值返回Order对象。

public class MyOrder
{
…. ….
public Order getOrder(String lower, String upper)
{
// 打开一个会话
Session sess = sf.openSession();
// HQL语句
String query = "select o from o "
+ "Order as o join o.products as p "
+ "where o.priceTotal > :priceTotalLower"
+ "and o.priceTotal< :priceTotalUpper";

Query q = sess.createQuery(query);
// 将两个参数传入HQL中
q.setDouble("priceTotalLower", Double.parseDouble(lower));
q.setDouble("priceTotalUpper", Double.parseDouble(upper));

List list = q.list();

Order o=(Order)list.iterator.next();

return o;

}
… …
}

  下面的main函数将测试MyOrder类

public class MyOrder
{
… …
public static void main(String args[])
{
Order o=MyOrder().getOrder(“100”, “300”);
System.out.println(“id=”+ o.id);
… …
}
}

   小结

  上述的代码演示了如何在Java中使用HQL,但HQL还有两点需要注意一下:

  1. HQL并不区分字母的大小写,但在HQL中的Java类和属性名必须和实际的类和属性名一致。如SELECT和select之间可以互换,但Order和order却代表不同的含义。

  2. 如果HQL中引用的类未被导入,在HQL中必须引用具体的包。如本例中,如果com.Order未被导入,在HQL中必须将Order写成com.Order。

=========================================================================================================================

1、简单属性查询【重要】

    *单一属性查询,返会属性结果集列表,元素类型和实体类中相应的类型一致

    *多个属性查询,多个属性查询返会对象数组,对象数组的长度取决于属性的个数,对象数组中元素的类型取决于属性在实体类中的类型

    *如果认为返会数组不够对象化,可以使用hql动态实例化Student对象

  

单一属性查询

  1. //返回结果集属性列表,元素类型和实体类中的属性类型一致  
  2.   
  3. Liststudents = session.createQuery("select name from Student").list();  
  4.   
  5. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  6.   
  7. Stringname = (String)iter.next();  
  8.   
  9. System.out.println(name);  
  10.   
  11. }  

 

 

多个属性查询

  1. //查询多个属性,返回对象数组集合  
  2.   
  3. //数组元素的类型与查询的属性类型一致  
  4.   
  5. //数组的长度与select中查询的属性个数一致  
  6.   
  7. Liststudents = session.createQuery("select id, name fromStudent").list();  
  8.   
  9. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  10.   
  11. Object[]obj = (Object[])iter.next();  
  12.   
  13. System.out.println(obj[0]+ ", " + obj[1]);  
  14.   
  15. }  


 

 

多个属性查询,返回Student

  1. //可以使用hql返回Student对象  
  2.   
  3. //需要提供构造函数  
  4.   
  5. Liststudents = session.createQuery("select new Student(id, name) fromStudent").list();  
  6.   
  7. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  8.   
  9. Studentstudent = (Student)iter.next();  
  10.   
  11. System.out.println(student.getId()+ ", " + student.getName());  
  12.   
  13. }  

 

 

使用别名

  1. //可以使用别名  
  2.   
  3. Liststudents = session.createQuery("select s.id, s.name from Students").list();  
  4.   
  5. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  6.   
  7. Object[]obj = (Object[])iter.next();  
  8.   
  9. System.out.println(obj[0]+ ", " + obj[1]);  
  10.   
  11. }  


 

 

  1. //可以采用as命名别名  
  2.   
  3. Liststudents = session.createQuery("select s.id, s.name from Student ass").list();  
  4.   
  5. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  6.   
  7. Object[]obj = (Object[])iter.next();  
  8.   
  9. System.out.println(obj[0]+ ", " + obj[1]);  
  10.   
  11. }  


 

 

 

2、实体对象查询【重要】

    * N + 1问题,就是发出了N+1条sql语句

     1:首先发出查询对象id列表的语句

     N:根据id到缓存中查询,如果缓存中不存在与之匹配的数据,那么会根据id发出相应的sql语句

   

    *list和iterate的区别?

     list:默认情况下list每次都会发出sql语句,list会将数据放到缓存中,而不利用缓存

     iterate:默认情况下iterate利用缓存,如果缓存中不存在会出现N+1问题

  1. //返回Student对象的集合  
  2.   
  3. //可以忽select关键字  
  4.   
  5. Liststudents = session.createQuery("from Student").list();  
  6.   
  7. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  8.   
  9. Studentstudent = (Student)iter.next();  
  10.   
  11. System.out.println(student.getName());  
  12.   
  13. }  
  14.   
  15.    
  16.   
  17. /**  
  18.   
  19.  * 采用list查询实体对象会发出一条查询语句,取得实体对象数据  
  20.   
  21.  *   
  22.   
  23.  * Hibernate: select student0_.id as id0_,student0_.name as name0_,   
  24.   
  25.  * student0_.createTime as createTime0_,student0_.classesid as classesid0_   
  26.   
  27.  * from t_student student0_  
  28.   
  29.  */  
  30.   
  31. Liststudents = session.createQuery("from Student").list();  
  32.   
  33. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  34.   
  35. Studentstudent = (Student)iter.next();  
  36.   
  37. System.out.println(student.getName());  
  38.   
  39. }  


 

3、条件查询【重要】

    *可以采用拼字符串的方式传递参数

  1. //可以拼串  
  2.   
  3. Liststudents = session.createQuery("select s.id, s.name from Student s wheres.name like '%0%'").list();  
  4.   
  5. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  6.   
  7. Object[]obj = (Object[])iter.next();  
  8.   
  9. System.out.println(obj[0]+ ", " + obj[1]);  
  10.   
  11. }  


 

    * 可以采用?来传递参数(索引从0开始)

  1. //可以使用?方式传递参数  
  2.   
  3. //索引从0开始,不同于jdbc从1开始  
  4.   
  5. //值能使用单引号引起来  
  6.   
  7. //Liststudents = session.createQuery("select s.id, s.name from Student s wheres.name like ?").list();  
  8.   
  9. //                        Queryquery  = session.createQuery("selects.id, s.name from Student s where s.name like ?");  
  10.   
  11. //                        query.setParameter(0,"%0%");  
  12.   
  13. //                        Liststudents = query.list();  
  14.   
  15.    
  16.   
  17. //方法链编程,建议采用此种方式  
  18.   
  19. Liststudents = session.createQuery("select s.id, s.name from Student s wheres.name like ?")  
  20.   
  21. .setParameter(0,"%0%")  
  22.   
  23. .list();  
  24.   
  25. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  26.   
  27. Object[]obj = (Object[])iter.next();  
  28.   
  29. System.out.println(obj[0]+ ", " + obj[1]);  
  30.   
  31. }  


 

    * 可以采用:参数名 来传递参数

 

 

  1. //可以采用:参数名 的方式传递参数  
  2.   
  3. Liststudents = session.createQuery("select s.id, s.name from Student s wheres.name like :myname")  
  4.   
  5. .setParameter("myname",  "%0%")  
  6.   
  7. .list();  
  8.   
  9. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  10.   
  11. Object[]obj = (Object[])iter.next();  
  12.   
  13. System.out.println(obj[0]+ ", " + obj[1]);  
  14.   
  15. }  


 

 

    *如果传递多个参数,可以采用setParamterList方法

  1. //采用?方式,查询学号为1,2,3,4,5的学生  
  2.   
  3. Liststudents = session.createQuery("select s.id, s.name from Student s wheres.id in(?, ?, ?, ?, ?)")  
  4.   
  5. .setParameter(0,1)  
  6.   
  7. .setParameter(1,2)  
  8.   
  9. .setParameter(2,3)  
  10.   
  11. .setParameter(3,4)  
  12.   
  13. .setParameter(4,5)  
  14.   
  15. .list();  
  16.   
  17. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  18.   
  19. Object[]obj = (Object[])iter.next();  
  20.   
  21. System.out.println(obj[0]+ ", " + obj[1]);  
  22.   
  23. }  


 

    *在hql中可以使用数据库的函数,如:date_format 

 

  1. //采用:参数名 方式,查询学号为1,2,3,4,5的学生  
  2.   
  3. Liststudents = session.createQuery("select s.id, s.name from Student s wheres.id in(:ids)")  
  4.   
  5. .setParameterList("ids",new Object[]{1, 2, 3, 4, 5})  
  6.   
  7. .list();  
  8.   
  9. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  10.   
  11. Object[]obj = (Object[])iter.next();  
  12.   
  13. System.out.println(obj[0]+ ", " + obj[1]);  
  14.   
  15. }  
  16.   
  17. //查询2009-08的学生,可以调用mysql的日期格式化函数  
  18.   
  19. Liststudents = session.createQuery("select s.id, s.name from Student s wheredate_format(s.createTime, '%Y-%m')=?")  
  20.   
  21. .setParameter(0,  "2009-08")  
  22.   
  23. .list();  
  24.   
  25. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  26.   
  27. Object[]obj = (Object[])iter.next();  
  28.   
  29. System.out.println(obj[0]+ ", " + obj[1]);  
  30.   
  31. }  
  32.   
  33.    
  34.   
  35. SimpleDateFormatsdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
  36.   
  37. //查询2009-08-01到2009-08-20的学生,可以调用mysql的日期格式化函数  
  38.   
  39. Liststudents = session.createQuery("select s.id, s.name from Student s wheres.createTime between ? and ?")  
  40.   
  41. .setParameter(0,sdf.parse("2009-08-01 00:00:00"))  
  42.   
  43. .setParameter(1,sdf.parse("2009-08-20 23:59:59"))  
  44.   
  45. .list();  
  46.   
  47. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  48.   
  49. Object[]obj = (Object[])iter.next();  
  50.   
  51. System.out.println(obj[0]+ ", " + obj[1]);  
  52.   
  53. }  

 

4、hibernate直接使用sql语句查询

  1. Liststudents = session.createSQLQuery("select * from t_student").list();  
  2.   
  3. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  4.   
  5. Object[]obj = (Object[])iter.next();  
  6.   
  7. System.out.println(obj[0]+ ", " + obj[1]);  
  8.   
  9. }  

 

5、外置命名查询

    * 在映射文件中使用<query>标签来定义hql

    * 在程序中使用session.getNamedQuery()方法得到hql查询串

   

  1.       
  2.   
  3. <querynamequeryname="queryStudent">  
  4.   
  5. <![CDATA[ 
  6.  
  7. selects from Student s where s.id <? 
  8.  
  9. ]]>  
  10.   
  11. </query>  

 

  1. List students =session.getNamedQuery("queryStudent")  
  2.   
  3. .setParameter(0,10)  
  4.   
  5. .list();  
  6.   
  7. for (Iteratoriter=students.iterator(); iter.hasNext();) {  
  8.   
  9. Studentstudent = (Student)iter.next();  
  10.   
  11. System.out.println(student.getName());  
  12.   
  13. }  

 

 

6、查询过滤器

    *在映射文件中定义过滤器参数

    *在类的映射中使用过滤器参数

    *在程序中必须显示的启用过滤器,并且为过滤器参数赋值

  1. <filter-defnamefilter-defname="testFilter">  
  2.   
  3. <filter-paramtypefilter-paramtype="integer" name="myid"/>  
  4.   
  5. </filter-def>  
  1. session.enableFilter("testFilter")  
  2.   
  3. .setParameter("myid",10);  
  4.   
  5. Liststudents = session.createQuery("from Student").list();  
  6.   
  7. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  8.   
  9. Studentstudent = (Student)iter.next();  
  10.   
  11. System.out.println(student.getName());  
  12.   
  13. }  

 

 

7、分页查询【重要】

    *setFirstResult(),从0开始

    *setMaxResults(),每页显示的记录数

  1. Liststudents = session.createQuery("from Student")  
  2.   
  3. .setFirstResult(1)  
  4.   
  5. .setMaxResults(2)  
  6.   
  7. .list();  
  8.   
  9. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  10.   
  11. Studentstudent = (Student)iter.next();  
  12.   
  13. System.out.println(student.getName());  
  14.   
  15. }  

 

 

8、对象导航查询【重要】

 

  1. Liststudents = session.createQuery("from Student s where s.classes.name like'%2%'")  
  2.   
  3. .list();  
  4.   
  5. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  6.   
  7. Studentstudent = (Student)iter.next();  
  8.   
  9. System.out.println(student.getName());  
  10.   
  11. }  


 

9、连接查询【重要】

    * 内连接

    * 外连接(左连接/右连接)left join     right join

  1. Liststudents = session.createQuery("select c.name, s.name from Student s joins.classes c")  
  2.   
  3. .list();  
  4.   
  5. for(Iterator iter=students.iterator(); iter.hasNext();) {  
  6.   
  7. Object[]obj = (Object[])iter.next();  
  8.   
  9. System.out.println(obj[0]+ ", " + obj[1]);  
  10.   
  11. }  

 

10、统计查询【重要】

  1. Longcount = (Long)session.createQuery("select count(*) fromStudent").uniqueResult();  


11、DML风格的操作(尽量少用,因为和缓存不同步)        

  1. @SuppressWarnings("unused")  
  2.   
  3. //Studentstudent = (Student)session.load(Student.class, 1);  
  4.   
  5. Studentstudent = (Student)session.get(Student.class, 1);  
  6.   
  7.    
  8.   
  9. //数据库表中更新了,但缓存不更新  
  10.   
  11. //所以一般不建议使用,除非有必须,如遇到性能问题  
  12.   
  13. session.createQuery("updateStudent s set s.name=? where s.id<?")  
  14.   
  15. .setParameter(0,"王五")  
  16.   
  17. .setParameter(1,5)  
  18.   
  19. .executeUpdate();  
  20.   
  21.    
  22.   
  23. //student= (Student)session.load(Student.class, 1);  
  24.   
  25. student= (Student)session.get(Student.class, 1);  
  26.   
  27. System.out.println("student.name="+ student.getName()); 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值