HQL的使用

简介: HQL(Hibernate Query Language) 是面向对象的查询语言, 它和 SQL 查询语言有些相似. 在 Hibernate 提供的各种检索方式中, HQL 是使用最广的一种检索方式

导读


HQL(Hibernate Query Language) 是面向对象的查询语言, 它和 SQL 查询语言有些相似. 在 Hibernate 提供的各种检索方式中, HQL 是使用最广的一种检索方式. 它有如下功能:

  1. 在查询语句中设定各种查询条件;
  2. 支持投影查询, 即仅检索出对象的部分属性;
  3. 支持分页查询;
  4. 支持连接查询;
  5. 支持分组查询, 允许使用 HAVING 和 GROUP BY 关键字;
  6. 提供内置聚集函数, 如 sum(), min() 和 max();
  7. 支持子查询;
  8. 支持动态绑定参数;
  9. 能够调用 用户定义的 SQL 函数或标准的 SQL 函数。

HQL 查询包括以下步骤:

  1. 获取Hibernate Session对象。
  2. 编写HQL语句
  3. 以HQL语句作为参数,调用Session的createQuery方法创建查询对象。
  4. 如果HQL语句包含参数,则调用Query的setXxx方法为参数赋值。
  5. 调用Query对象的list()或uniqueResult()方法返回查询结果列表(持久化实体集)

Qurey 接口支持方法链编程风格, 它的 setXxx() 方法返回自身实例, 而不是 void 类型,因此可以写类似于.setXxx().setXxx().setXxx()...样式的语句。

HQL vs SQL


HQL 查询语句是面向对象的, Hibernate 负责解析 HQL 查询语句, 然后根据对象-关系映射文件中的映射信息, 把 HQL 查询语句翻译成相应的 SQL 语句。HQL 查询语句中的主体是域模型中的类及类的属性。

SQL 查询语句是与关系数据库绑定在一起的。SQL 查询语句中的主体是数据库表及表的字段。


一:属性查询(SimplePropertyQuery)

1,单一属性查询

  • 返回结果集属性列表,元素类型和实体类中相应的属性一致

2, 多个属性查询,

  • 返回集合中的元素是object数组,
  • 其中数组元素的类型和对应属性在实体类中的类型一致
  1. for(Iterator iterator=list.iterator();iterator.hasNext();)
  2. {
  3. Object[] obj=(Object[])iterator.next();
  4. System.out.println("sid:="+obj[0].toString()+"   sname:="+obj[1]);
  5. }

  6. List<Object[]> list = session.createQuery(
  7. "select c.cid,c.cname from Customer c").list();
  8. for (Object[] objects: list)
  9. {
  10.   System.out.println(Arrays.toString(objects));
  11. }



3, 通过hql来动态实例化对象

  1. List list=session.createQuery("select new Student(sid,sname) from Student").list();
  2. for (Iterator iterator=list.iterator();iterator.hasNext();){
  3.    Student student=(Student) iterator.next();
  4.    System.out.println("sid:="+student.getSid()+"   sname:="+student.getSname());
  5. }



4,采用别名查询

  1. //List list=session.createQuery("select s.sid, s.sname from Student as s").list();
  2. List list=session.createQuery("select s.sid, s.sname from Student s").list();




二:简单对象查询(SimplyObjectQuery)


1,返回对象集合(可以忽略select)

  1. //List list=session.createQuery("from Student").list();
  2. //List list=session.createQuery("from Student as s").list();
  3. List list=session.createQuery("from Student s").list();


  • 在HQL语句中,本身大小写无关,但是其中出现的类名和属性名必须注意大小写区分。
  • 同时,在Hibernate中,查询的目标实体存在继承关系的判定,如果from User将返回所有User以及User子类的记录。
  • 假设系统中存在User的两个子类:SysAdmin和SysOperator,那么该hql语句返回的记录将包含这两个子类的所有数据,即使SysAdmin和SysOperator分别对应了不同的库表。


2,采用select查询对象

  • 必须要使用别名
  1. //List list=session.createQuery("select s from Student as s").list();
  2. List list=session.createQuery("select s from Student s").list();


3,查询单一对象的操作

  1. // 不支持 select * from Customer写法.可以写成 select 别名 from Customer as 别名;
  2. Object obj=session.createQuery("select s from Student s where s.sid=:mysid")
  3. .setParameter("mysid", 12)
  4. .uniqueResult();


4,分页查询

  • setFirstResult(int firstResult)
  • Set the first row to retrieve.
  • If not set, rows will be retrieved beginnning from row 0.
  • firstResult - a row number, numbered from 0
  1. List list=session.createQuery("select s from Student s")
  2.   .setFirstResult(5)
  3.   .setMaxResults(5)
  4.   .list();



三:简单条件查询(SimpleConditionQuery)

1,可以拼凑字符串

  1. List stuList=session.createQuery
  2. ("select s.sid, s.sname from Student as s where s.sname like '%1%'").list();


SQL注入

在解释参数绑定的使用时,我们先来解释一下什么是SQL注入。

SQL Injection是常见的系统攻击手短,这种攻击方式的目标是针对由SQL字符串拼接造成的漏洞。如,为了实现用户登录功能,我们编写了以下代码:

  1. FROM User user WHERE user.name='"+username+"' AND user.password='"+password+"'


从逻辑上讲,该HQL并没有错误,我们根据用户名和密码从数据库中读取相应的记录,如果找到记录,则认为用户身份合法。

假设这里的变量username和password是来自于网页上输入框的数据。现在我们来做个尝试,在登录网页上输入用户名:"'Erica' or 'x'='x'",密码随意,也可以登录成功。

此时的HQL语句为:

  1. FROM User user WHERE user.name='Erica' OR 'x'='x' AND user.password='fasfas'


此时,用户名中的OR 'x'='x'被添加到了HQL并作为子句执行,where逻辑为真,而密码是否正确就无关紧要。

这就是SQL Injection攻击的基本原理,而字符串拼接而成的HQL是安全漏洞的源头。参数的动态绑定机制可以妥善处理好以上问题。

Hibernate提供顺序占位符以及引用占位符,将分别举例说明:

2,注意:可以采用 ?占位的方式来传递参数

  • 参数的索引从0开始
  • 传递的参数值不能用''号括起来
  • 注意方法链编程
  1. /*
  2. *Query query=ession.createQuery("select s.sid, s.sname from Student as s where s.sname like ?");
  3. *query.setParameter(0, "%1%");
  4. *List stuList=query.list();
  5. */
  6. List stuList=session.createQuery("select s.sid, s.sname from Student as s where s.sname like ?")
  7.    .setParameter(0, "%1%")
  8.    .list();



3,通过 参数名(:参数名)传参的方式进行查询

  1. /*Object obj=session.createQuery("select s.sid, s.sname from Student as s where s.sname like :myname and s.sid=:myid")
  2.    .setParameter("myname", "%1%")
  3.    .setParameter("myid", 98)
  4.    .uniqueResult();//.list();
  5. Object[] objs=(Object[])obj;
  6. System.out.println("s.sid:="+objs[0].toString()+"  s.sname:="+objs[1].toString());
  7. */
  8. List stuList=session.createQuery("select s.sid, s.sname from Student as s where s.sname like :myname and s.sid=:myid")
  9.    .setParameter("myname", "%1%")
  10.    .setParameter("myid", 98)
  11.    .list();



4,传递多个参数的查询

  • 支持in查询,需要setParameterList("myids", new Object[]{12,13,14})的方式进行参数传递
  1. List stuList=session.createQuery("select s.sid, s.sname from Student as s where s.id in(:myids)")
  2.    .setParameterList("myids", new Object[]{12,13,14})
  3.    .list();



5, 查询2009年2月入学的学员信息

  • 支持Sql函数查询
  1. List stuList=session.createQuery("select s.sid, s.sname ,s.schoolDate from Student as s where
  2.  year(s.schoolDate)=:myyear and month(s.schoolDate)=:mymonth")
  3.    .setParameter("myyear", 2009)
  4.    .setParameter("mymonth", 2)
  5.    .list();


6,查询2009-02-10到2009-02-15号入学的学员信息

  • 注意:在进行日期查询时
  • setParameter("startDate", formatter.parse("2009-02-10 00:00:00"))
  • 方法中第二个参数是一个Object对象(Date类型对象)
  1. SimpleDateFormat formatter=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  2. List stuList=session.createQuery("select s.sid, s.sname ,s.schoolDate
  3.  from Student as s where s.schoolDate between :startDate and :endDate")
  4.    .setParameter("startDate", formatter.parse("2009-02-10 00:00:00"))
  5.    .setParameter("endDate", formatter.parse("2009-02-15 23:59:59"))
  6.    .list();


7,绑定实体

  1. List<Order> list = session
  2. .createQuery("from Order o where o.customer = ?")
  3. .setEntity(0, customer).list();
  4. for (Order order : list) {
  5. System.out.println(order);
  6. }



四:原生SQL语句查询(SqlQueryTest_4)

  • 这里用的是createSQLQuery().list();
  1. List stuList=session.createSQLQuery("select * from Student").list();


五:外置命名查询(NamedQueryTest)

* 1.在映射文件中采用<query>标签来定义HQL语句,不必限定文件!!!

  • 注意:映射文件中的HQL语句不可以出错,否则会导致 QuerySyntaxException,
  • 特别是要查询的实体类名写错的情况。(Error in named query: selectAllStudents org.hibernate.hql.ast.QuerySyntaxException: Students is not mapped)。


如果我们将HQL代码写在类中,那么编译后我们非常难以维护,为了后期代码的可维护行,我们需要将HQL代码写在对应类的.hbm.xml文件中,例如:

  1. <queryname="queryUserRanage">
  2.    FROM User WHERE id BETWEEN ? AND ?
  3. </query> 

在Java代码中

  1. Query query=session.getNamedQuery("queryUserRanage");
  2. List<User> list=query
  3.                .setParameter(0, 10)
  4.                .setParameter(1, 20)
  5.                .list();
  6. for(User user:list){
  7.    System.out.println(user);
  8. }

通常我们使用参数的方式,这样可以很直观知道每个参数的作用

  1. <queryname="queryUserRanage">
  2.    FROM User WHERE id BETWEEN :minId AND :maxId
  3. </query>

  但是如果在SQL语句中存在>、<等xml中特殊字符,这些字符在xml中都有特殊的意义,所有我们不能直接这样写

  【Error】

  1. <queryname="queryUserRanage">
  2.    FROM User WHERE id > :minId AND id< :maxId
  3. </query>

  【Right】

  1. <queryname="queryUserRanage">
  2.    FROM User WHERE id &gt; :minId AND &lt; :maxId
  3. </query>

虽然上面的方式我们可以正确执行,但是代码显示不够友好,所有我们推荐使用下面的一种方式

  1. <queryname="queryUserRanage">
  2.    <![CDATA[FROM User WHERE id > :minId AND < :maxId]]
  3. </query>

  CDATA代码块说明在代码中的语句不需要转义,我们一个HQL都推荐用CDATA块来包裹着


六:对象导航查询(ObjectNavQueryTest)

  1. List list= session.createQuery("select s.sname from Student as s where s.clsInfo.cname like :myname")
  2.      .setParameter("myname", "%1%")
  3.      .list();


七:联合查询(JoinQueryTes)

1,联合查询* 内连接查询

  • 获取已经参加班级报名的所有学生名号和其所在班级名称
  1. List list=session.createQuery("select s.sname,c.cname from Student as s join s.clsInfo as c").list();


2,联合查询* 左连接查询

  • 获取所有班级名称和其学员姓名
  • 以班级信息表为主表,进行左外连接查询
  1. List list=session.createQuery("select c.cname ,s.sname from ClassInfo as c left join c.studentSet as s").list();


3,联合查询 * 右连接查询

  • 获取所有学员姓名和其所属班级名称
  • 以学员信息表为主表,进行右外连接查询
  1. List list=session.createQuery("select s.sname, c.cname  from ClassInfo as c right join c.studentSet as s").list();


八:(StateQueryTest)

1,聚合函数使用

  1. //List list=session.createQuery("select count(*) from Student").list();
  2. //long count=(Long) list.get(0);
  3. long count=(Long) session.createQuery("select count(*) from Student").uniqueResult();


2,分组查询

  1. hql="SELECT age, COUNT(age) num FROM User WHERE age>10 GROUP BY age HAVING COUNT(age)>1
  2.     ORDER BY num DESC";
  3. 在HAVING中,不能使用别名num,但在ORDER BY中可以使用别名num



  1. List list=session.createQuery("select c.cname, count(s) from Student as s inner join
  2. s.clsInfo as c group by c.cname order by c.cname").list();


3,分页查询

  1. Query query = session.createQuery("from Order");
  2. query.setFirstResult(20);
  3. query.setMaxResults(10);
  4. List<Order> list = query.list();
  5. for (Order order : list) {
  6. System.out.println(order);
  7. }


9.更新和删除

  1. String hql = "UPDATE User SET age = 18 WHERE id = 1";
  2. int result = session.createQuery(hql).executeUpdate();


上述代码利用HQL语句实现了更新操作。对于单个对象的更新也许代码量并没有减少太多,但如果对于批量更新操作,其便捷性以及性能的提高就相当可观。

例如,以下代码将所有用户的年龄属性更改为10

  1. UPDATE User SET age = 18


HQL的delete子句使用同样很简单,例如以下代码删除了所有年龄大于18的用户记录:

  1. DELETE User WHERE age > 18


不过,需要注意的是,在HQL delete/update子句的时候,必须特别注意它们对缓存策略的影响,极有可能导致缓存同步上的障碍。


10,关联查询

关于这部分的内容,参考了很多书上的资料,但都感觉讲的不够清晰,也就是说没有结合到实际的情况中去。下面将按照一个视频教程上的顺序来介绍关联查询。

关于这部分的知识点,是鉴于已经对关联连接查询有所了解的基础上,比如懂得什么是左外连接、内连接等。 下面就开始总结:

HQL迫切左外连接

LEFT JOIN FETCH关键字表示使用迫切左外连接策略。 首先看一下例子中的实体类,这是一个双向1-N的映射

实体类:

  1. public class Department {
  2.    private Integer id;
  3.    private String name;
  4.    private Set<Employee> emps = new HashSet<>();
  5.  //省却get和set方法
  6. }
  7. public class Employee {
  8.    private Integer id;
  9.    private String name;
  10.    private float salary;
  11.    private String email;
  12.    private Department dept;
  13.  //省去get和set方法
  14. }


迫切左外连接

  1. String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.emps";
  2. Query query = session.createQuery(hql);
  3. List<Department> depts = query.list();
  4. System.out.println(depts.size());
  5. for (Department dept : depts) {
  6.    System.out.println(dept.getName() + "-" + dept.getEmps().size());
  7. }


上面的例子中是想得到所有的部门以及其中的员工。我们通过DISTINCT进行去重。 注意的点:

  • list()方法中返回的集合中存放实体对象的引用。每个Department对象关联的Employee集合都将被初始化,存放所有关联的Employee的实体对象
  • 查询结果中可能会包含重复元素,可以通过DISTINCT关键字去重,同样由于list中存放的实体对象的引用,所以可以通过HashSet来过滤重复对象。例如:
  1. List<Department> depts = query.list();
  2. depts = new ArrayList<>(new LinkedHashSet(depts));

左外连接

  1. String hql = "FROM Department d LEFT JOIN d.emps";
  2. Query query = session.createQuery(hql);
  3. List<Object[]> result = query.list();
  4. System.out.println(result);
  5. for (Object[] objs : result) {
  6.    System.out.println(Arrays.asList(objs));
  7. }


注意的是:通过左外连接返回的list是一个包含Department和与之连接的Employee的object数组。所以我们还需要对数组进行处理,并且有重复。鉴于此,我们可以通过DISTINCT进行处理。

  1. String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN d.emps";
  2.        Query query = session.createQuery(hql);
  3.        List<Department> depts = query.list();
  4.        System.out.println(depts.size());
  5.        for (Department dept : depts) {
  6.            System.out.println(dept.getName() + ", " + dept.getEmps().size());
  7.        }


注意:

  • list方法返回的集合中存放的是对象数组类型
  • 根据配置文件来决定Employee集合的检索策略,比如是fetch、lazy啊或者怎样,还不是像迫切左外连接那样。

我们真正进行的过程中,一般都会使用迫切左外连接。因为迫切左外连接只发送了一条SQL语句将所有信息都查出来,而左外连接就算不使用集合的对象,也会进行查询,而当真正使用集合的时候,又会再去查询。所以性能上迫切左外连接要好。

11,子查询

子查询可以在HQL中利用另外一条HQL的查询结果。 例如:

  1. FROM User user WHERE (SELECT COUNT(*) FROM user.address) > 1


HQL中,子查询必须出现在where子句中,且必须以一对圆括号包围。


来源: http://carolli.iteye.com/blog/909542

来源: https://www.w3cschool.cn/hibernate_articles/gapw1ioo.html

来源: http://www.cnblogs.com/caoyc/p/5606444.html




目录
相关文章
|
4月前
|
缓存 Java 数据库连接
什么是 Hibernate 查询语言或 HQL?
【8月更文挑战第21天】
141 0
|
Java 数据库连接
HQL数据查询(Hibernate推荐)
HQL数据查询(Hibernate推荐)
128 0
常用的Hql语句
本文主要讲常用的Hql语句
146 0
|
SQL Java 数据库连接
|
Java 数据库连接 数据库
Hibernate查询语言(HQL)
Hibernate查询语言(HQL)与SQL(结构化查询语言)相同,但不依赖于数据库表。 我们在HQL中使用类名,而不是表名,它是数据库独立的查询语言。
3324 0
|
SQL Java 数据库连接
HQL查询
一、前言        HQL(Hibernate QueryLanguage) 是面向对象的查询语言,它和SQL查询语言有些相似。 二、语法 1、Select/update/delete…… from …… where …… group by …… having ……orde...
1089 0