深入HQL学习以及HQL和SQL的区别_hql sql-程序员宅基地

技术标签: 面向对象  hql  数据库相关  JAVA相关  hibernate  数据库  sql  

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 查询语句中的主体是数据库表及表的字段。

HQL实用技术


实体查询

最简单实体查询例子:

String hql = "from User";
Query query = session.createQuery(hql);
List<User> list = query.list();

上面的HQL语句将取出User的所有对应记录为:select user0_.U_ID as U_ID1_0_,user0_.U_NAME as U_NAME2_0_,user0_.U_AGE as U_AGE3_0_ from USERS user0_

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

Where子句: 如果我们想取出名为“Erica”的用户记录,可以通过Where子句加以限定(其中AS可以省略):

FROM User AS user WHERE user.name='Erica'

where子句中,我们可以通过比较操作符指定甄选条件,如: =, , <, >, <=, >=, between, not between, in ,not in, is, like等。同时,在where子句中可以使用算术表达式。 几个简单实例:

FROM User user WHERE user.age<20
FROM User user WHERE user.name IS null
FROM User user WHERE user.name LIKE 'Er%'
FROM User user WHERE (user.age % 2 = 1)
FROM User user WHERE (user.age<20) AND (user.name LIKE '%Er')

属性查询

有时,我们需要的数据可能仅仅是实体对象的某个属性(库表记录中的某个字段信息)。通过HQL可以简单的做到这一点。

String hql = "SELECT user.name FROM User user";
List list = session.createQuery(hql).list();
Iterator it = list.iterator();
while(it.hasNext()){
    System.out.println(it.next());
}

上例中,我们指定了只需要获取User的name属性。此时返回的list数据结构中,每个条目都是一个String类型的name数据。 我们也可以通过一条HQL获取多个属性:

public void test() {
        String hql = "SELECT user.name,user.age FROM User user";
        List list = session.createQuery(hql).list();
        Iterator it = list.iterator();
        while(it.hasNext()){
            Object[] results = (Object[]) it.next();
            System.out.println(results[0]+","+results[1]);
        }
    }

而此时,返回的list数据结构中,每个条目都是一个对象数组(Object[]),其中依次包含了我们所获取的属性数据。

除此之外,我们也可以通过在HQL中动态的构造对象实例的方法对这些平面化的数据进行封装。

String hql = "SELECT new User(user.uName,user.uAge) FROM User user";
        List list = session.createQuery(hql).list();
        Iterator it = list.iterator();
        while(it.hasNext()){
            User user = (User) it.next();
            System.out.println(user);
        }

通过在HQL中动态的构造对象实例,我们实现了对查询结果的对象化封装。此时在查询结果中的User对象仅仅是一个普通的Java对象,仅用于对查询结果的封装,除了在构造时赋予的属性值外,其他属性均为未赋值状态。同时,在实体类中,要提供包含构造属性的构造方法,并且顺序要相同。

与此同时,我们也可以在HQL的select子句中使用统计函数或者利用DSITINCT关键字,剔除重复记录。

SELECT COUNT(*),MIN(user.age) FROM User user
SELECT DISTINCT user.name FROM User user

实体更新与删除

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

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

UPDATE User SET age = 18

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

DELETE User WHERE age > 18

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

分组与排序

Order by子句

举例说明:

FROM User user ORDER BY user.name

默认情况下是按照升序排序,当然我们可以指定排序策略:

FROM User user ORDER BY user.name DESC

order by子句可以指定多个排序条件:

FROM User user ORDER BY user.name, user.age DESC

Group by子句

通过Group by可进行分组统计,如果下例中,我们通过Group by子句实现了同龄用户的统计:

SELECT COUNT(user),user.age FROM User user GROUP BY user.age

通过该语句,我们获得了一系列的统计数据。对于Group by子句获得的结果集而言,我们可以通过Having子句进行筛选。例如,在上例中,我们对同龄用户进行了统计,获得了每个年龄层次中的用户数量,假设我们只对超过10人的年龄组感兴趣,可用以下语句实现:

SELECT COUNT(user),user.age FROM User user GROUP BY user.age HAVING COUNT(user) > 10

参数绑定

SQL注入

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

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

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

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

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

此时的HQL语句为:

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

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

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

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

顺序占位符:

String hql = "from User user WHERE user.name = ? AND user.age = ?";
        List<User> list = session.createQuery(hql).setString(0, "Erica")
                .setInteger(1, 10).list();

引用占位符:

String hql = "from User user WHERE user.uName = :name AND user.uAge = :age";
        List<User> list = session.createQuery(hql).setString("name", "Erica")
                .setInteger("age", 10).list();

我们甚至还可以用一个JavaBean来封装查询参数。

参数绑定机制可以使得查询语法与具体参数数值相互独立。这样,对于参数不同,查询语法相同的查询操作,数据库即可实施性能优化策略。同时,参数绑定机制也杜绝了参数值对查询语法本身的影响,这也就是避免了SQL Injection的可能。

引用查询

我们可能遇到过如下编码规范:“代码中不允许出现SQL语句”。

SQL语句混杂在代码之间将破坏代码的可读性,并似的系统的可维护性降低。为了避免这样的情况,我们通常采取将SQL配置化的方式,也就是说,将SQL保存在配置文件中。Hibernate提供了HQL可配置化的内置支持。

我们可以在实体映射文件中,通过query节点定义查询语句(与class节点同级):

<query name="queryTest"><![CDATA[FROM User user where user.uAge < 20]]></query>

需要注意的是,我们是将HQL语句写入到了xml文件中,所以可能会造成冲突。例如HQL语句的的“<”(小于)与xml的语法有冲突。所以我们会用CDATA将其包裹。

之后我们可以通过session的getNamedQuery方法从配置文件中调用对应的HQL,如:

Query query = session.getNamedQuery("queryTest");
        List<User> list = query.list();
        for(User user : list){
            System.out.println(user);
        }

关联查询

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

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

HQL迫切左外连接

LEFT JOIN FETCH关键字表示使用迫切左外连接策略。 首先看一下例子中的实体类,这是一个双向1-N的映射(关于1-N的映射在之前的博客中有介绍Hibernate关系映射2:双向1-N关联).

实体类:

public class Department {

    private Integer id;
    private String name;
    private Set<Employee> emps = new HashSet<>();
  //省却get和set方法
}
public class Employee {

    private Integer id;
    private String name;
    private float salary;
    private String email;

    private Department dept;
  //省去get和set方法
}
迫切左外连接
String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.emps";
Query query = session.createQuery(hql);
List<Department> depts = query.list();
System.out.println(depts.size());
for (Department dept : depts) {
    System.out.println(dept.getName() + "-" + dept.getEmps().size());
}

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

  • list()方法中返回的集合中存放实体对象的引用。每个Department对象关联的Employee集合都将被初始化,存放所有关联的Employee的实体对象
  • 查询结果中可能会包含重复元素,可以通过DISTINCT关键字去重,同样由于list中存放的实体对象的引用,所以可以通过HashSet来过滤重复对象。例如:
List<Department> depts = query.list();
depts = new ArrayList<>(new LinkedHashSet(depts));
左外连接
String hql = "FROM Department d LEFT JOIN d.emps";
Query query = session.createQuery(hql);
List<Object[]> result = query.list();
System.out.println(result);
for (Object[] objs : result) {
    System.out.println(Arrays.asList(objs));
}

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

String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN d.emps";
        Query query = session.createQuery(hql);

        List<Department> depts = query.list();
        System.out.println(depts.size());

        for (Department dept : depts) {
            System.out.println(dept.getName() + ", " + dept.getEmps().size());
        }

注意:

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

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

子查询

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

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

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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_28633249/article/details/77884062

智能推荐

hive使用适用场景_大数据入门:Hive应用场景-程序员宅基地

文章浏览阅读5.8k次。在大数据的发展当中,大数据技术生态的组件,也在不断地拓展开来,而其中的Hive组件,作为Hadoop的数据仓库工具,可以实现对Hadoop集群当中的大规模数据进行相应的数据处理。今天我们的大数据入门分享,就主要来讲讲,Hive应用场景。关于Hive,首先需要明确的一点就是,Hive并非数据库,Hive所提供的数据存储、查询和分析功能,本质上来说,并非传统数据库所提供的存储、查询、分析功能。Hive..._hive应用场景

zblog采集-织梦全自动采集插件-织梦免费采集插件_zblog 网页采集插件-程序员宅基地

文章浏览阅读496次。Zblog是由Zblog开发团队开发的一款小巧而强大的基于Asp和PHP平台的开源程序,但是插件市场上的Zblog采集插件,没有一款能打的,要么就是没有SEO文章内容处理,要么就是功能单一。很少有适合SEO站长的Zblog采集。人们都知道Zblog采集接口都是对Zblog采集不熟悉的人做的,很多人采取模拟登陆的方法进行发布文章,也有很多人直接操作数据库发布文章,然而这些都或多或少的产生各种问题,发布速度慢、文章内容未经严格过滤,导致安全性问题、不能发Tag、不能自动创建分类等。但是使用Zblog采._zblog 网页采集插件

Flink学习四:提交Flink运行job_flink定时运行job-程序员宅基地

文章浏览阅读2.4k次,点赞2次,收藏2次。restUI页面提交1.1 添加上传jar包1.2 提交任务job1.3 查看提交的任务2. 命令行提交./flink-1.9.3/bin/flink run -c com.qu.wc.StreamWordCount -p 2 FlinkTutorial-1.0-SNAPSHOT.jar3. 命令行查看正在运行的job./flink-1.9.3/bin/flink list4. 命令行查看所有job./flink-1.9.3/bin/flink list --all._flink定时运行job

STM32-LED闪烁项目总结_嵌入式stm32闪烁led实验总结-程序员宅基地

文章浏览阅读1k次,点赞2次,收藏6次。这个项目是基于STM32的LED闪烁项目,主要目的是让学习者熟悉STM32的基本操作和编程方法。在这个项目中,我们将使用STM32作为控制器,通过对GPIO口的控制实现LED灯的闪烁。这个STM32 LED闪烁的项目是一个非常简单的入门项目,但它可以帮助学习者熟悉STM32的编程方法和GPIO口的使用。在这个项目中,我们通过对GPIO口的控制实现了LED灯的闪烁。LED闪烁是STM32入门课程的基础操作之一,它旨在教学生如何使用STM32开发板控制LED灯的闪烁。_嵌入式stm32闪烁led实验总结

Debezium安装部署和将服务托管到systemctl-程序员宅基地

文章浏览阅读63次。本文介绍了安装和部署Debezium的详细步骤,并演示了如何将Debezium服务托管到systemctl以进行方便的管理。本文将详细介绍如何安装和部署Debezium,并将其服务托管到systemctl。解压缩后,将得到一个名为"debezium"的目录,其中包含Debezium的二进制文件和其他必要的资源。注意替换"ExecStart"中的"/path/to/debezium"为实际的Debezium目录路径。接下来,需要下载Debezium的压缩包,并将其解压到所需的目录。

Android 控制屏幕唤醒常亮或熄灭_android实现拿起手机亮屏-程序员宅基地

文章浏览阅读4.4k次。需求:在诗词曲文项目中,诗词整篇朗读的时候,文章没有读完会因为屏幕熄灭停止朗读。要求:在文章没有朗读完毕之前屏幕常亮,读完以后屏幕常亮关闭;1.权限配置:设置电源管理的权限。

随便推点

目标检测简介-程序员宅基地

文章浏览阅读2.3k次。目标检测简介、评估标准、经典算法_目标检测

记SQL server安装后无法连接127.0.0.1解决方法_sqlserver 127 0 01 无法连接-程序员宅基地

文章浏览阅读6.3k次,点赞4次,收藏9次。实训时需要安装SQL server2008 R所以我上网上找了一个.exe 的安装包链接:https://pan.baidu.com/s/1_FkhB8XJy3Js_rFADhdtmA提取码:ztki注:解压后1.04G安装时Microsoft需下载.NET,更新安装后会自动安装如下:点击第一个傻瓜式安装,唯一注意的是在修改路径的时候如下不可修改:到安装实例的时候就可以修改啦数据..._sqlserver 127 0 01 无法连接

js 获取对象的所有key值,用来遍历_js 遍历对象的key-程序员宅基地

文章浏览阅读7.4k次。1. Object.keys(item); 获取到了key之后就可以遍历的时候直接使用这个进行遍历所有的key跟valuevar infoItem={ name:'xiaowu', age:'18',}//的出来的keys就是[name,age]var keys=Object.keys(infoItem);2. 通常用于以下实力中 <div *ngFor="let item of keys"> <div>{{item}}.._js 遍历对象的key

粒子群算法(PSO)求解路径规划_粒子群算法路径规划-程序员宅基地

文章浏览阅读2.2w次,点赞51次,收藏310次。粒子群算法求解路径规划路径规划问题描述    给定环境信息,如果该环境内有障碍物,寻求起始点到目标点的最短路径, 并且路径不能与障碍物相交,如图 1.1.1 所示。1.2 粒子群算法求解1.2.1 求解思路    粒子群优化算法(PSO),粒子群中的每一个粒子都代表一个问题的可能解, 通过粒子个体的简单行为,群体内的信息交互实现问题求解的智能性。    在路径规划中,我们将每一条路径规划为一个粒子,每个粒子群群有 n 个粒 子,即有 n 条路径,同时,每个粒子又有 m 个染色体,即中间过渡点的_粒子群算法路径规划

量化评价:稳健的业绩评价指标_rar 海龟-程序员宅基地

文章浏览阅读353次。所谓稳健的评估指标,是指在评估的过程中数据的轻微变化并不会显著的影响一个统计指标。而不稳健的评估指标则相反,在对交易系统进行回测时,参数值的轻微变化会带来不稳健指标的大幅变化。对于不稳健的评估指标,任何对数据有影响的因素都会对测试结果产生过大的影响,这很容易导致数据过拟合。_rar 海龟

IAP在ARM Cortex-M3微控制器实现原理_value line devices connectivity line devices-程序员宅基地

文章浏览阅读607次,点赞2次,收藏7次。–基于STM32F103ZET6的UART通讯实现一、什么是IAP,为什么要IAPIAP即为In Application Programming(在应用中编程),一般情况下,以STM32F10x系列芯片为主控制器的设备在出厂时就已经使用J-Link仿真器将应用代码烧录了,如果在设备使用过程中需要进行应用代码的更换、升级等操作的话,则可能需要将设备返回原厂并拆解出来再使用J-Link重新烧录代码,这就增加了很多不必要的麻烦。站在用户的角度来说,就是能让用户自己来更换设备里边的代码程序而厂家这边只需要提供给_value line devices connectivity line devices