Hibernate学习笔记(四)——HQL

2024-05-28 01:38
文章标签 学习 笔记 hibernate hql

本文主要是介绍Hibernate学习笔记(四)——HQL,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、HQL基本概念

HQL是Hibernate Query Language的简称,它是面向对象的查询语句,完整的HQL语句形式如下: select…… from …… where …… group by …… having …… order by …… asc/desc,其中select子句是用来指定查询结果中的对象和属性,并指定以何种数据类型返回;from子句用来指定HQL语句的查询目标,即映射配置的持久化类及其属性;where子句是用来设置查询条件,限制返回结果和范围的;group by子句用来进行分组;having子句用来对分组进行条件限制;order by子句是用来对查询到的结果进行排序的。从语句的格式来看,HQL与SQL语句是类似的,但是HQL与SQL有本质的不同,HQL是面向对象的查询语言,而SQL是面向数据库的;另一点不同的是,HQL语句最简单只需要一个from子句就可以了,其他的都可以省略,而SQL是不行的。
刚开始使用HQL时特别需要注意的是HQL因为是面向对象的,所以使用HQL时对Java类和属性是大小写敏感的,这与SQL有本质的不同,SQL语句对表格和列都不是大小写敏感的,但是在HQL中,HQL的关键字是不区分大小写的,例如查询子句的select,既可以是大写也可以是小写。
在Hibernate中要使用HQL,需要使用Query接口,通过这个接口解析HQL语句,然后根据配置信息,将HQL转换为对应的SQL,接着去数据库执行相应的查询操作。而使用Query接口的过程是这样的:首先使用Session的createQuery(String hql)方法创建一个Query实例,将相应的hql语句作为参数传进这个方法;接着使用Query的list()方法执行HQL查询,list()方法返回结果数据类型为java.util.List,List集合中存放符合查询条件的持久化对象。
下面我们就通过实例来详细讲解一下HQL的使用。

二、使用前准备

在讲解HQL之前,需要在数据库中创建相应的表并且配置相应的实体类和映射文件,在这个例子中我们创建两张表,商家表seller和商品表commodity,其中商品表通过外键与商家表关联,一个商家可能对应多个商品。建表语句如下,首先是商家表seller:
CREATE TABLE `seller` (`Id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(100) DEFAULT NULL,`tel` varchar(1000) DEFAULT NULL,`address` varchar(2000) DEFAULT NULL,`website` varchar(500) DEFAULT NULL,`star` int(11) DEFAULT NULL,`business` varchar(2000) DEFAULT NULL,PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
接着是商品表commodity:
CREATE TABLE `commodity` (`Id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(100) DEFAULT NULL,`price` double(11,2) DEFAULT NULL,`unit` varchar(50) DEFAULT NULL,`category` varchar(100) DEFAULT NULL,`description` varchar(1000) DEFAULT NULL,`seller` int(11) DEFAULT NULL,PRIMARY KEY (`Id`),KEY `fk_commodity_1_idx` (`seller`),CONSTRAINT `fk_commodity_1` FOREIGN KEY (`seller`) REFERENCES `seller` (`Id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
在表中插入一些测试数据,seller表:

commodity表:
然后创建两个实体类,Seller和Commodity。
package com.imooc.vo;public class Seller {private int id;private String name;private String tel;private String address;private String website;private int star;private String business;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getTel() {return tel;}public void setTel(String tel) {this.tel = tel;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public String getWebsite() {return website;}public void setWebsite(String website) {this.website = website;}public int getStar() {return star;}public void setStar(int star) {this.star = star;}public String getBusiness() {return business;}public void setBusiness(String business) {this.business = business;}@Overridepublic String toString() {return "Seller [id=" + id + ", name=" + name + ", tel=" + tel+ ", address=" + address + ", website=" + website + ", star="+ star + ", business=" + business + "]";}public Seller() {}}
package com.imooc.vo;public class Commodity {private int id;private String name;private double price;private String unit;private String category;private String description;private Seller seller;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}public String getUnit() {return unit;}public void setUnit(String unit) {this.unit = unit;}public String getCategory() {return category;}public void setCategory(String category) {this.category = category;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public Seller getSeller() {return seller;}public void setSeller(Seller seller) {this.seller = seller;}@Overridepublic String toString() {return "Commodity [id=" + id + ", name=" + name + ", price=" + price+ ", unit=" + unit + ", category=" + category+ ", description=" + description + ", seller=" + seller.getId() + "]";}}
接着就是配置这两个实体类的映射文件,Seller.hbm.xml和Commodity.hbm.xml。
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-9-26 20:39:32 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping><class name="com.imooc.vo.Seller" table="seller"><id name="id" type="int"><column name="id" /><generator class="native" /></id><property name="name" type="java.lang.String"><column name="name" /></property><property name="tel" type="java.lang.String"><column name="tel" /></property><property name="address" type="java.lang.String"><column name="address" /></property><property name="website" type="java.lang.String"><column name="website" /></property><property name="star" type="int"><column name="star" /></property><property name="business" type="java.lang.String"><column name="business" /></property></class>
</hibernate-mapping>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-9-26 20:39:32 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping><class name="com.imooc.vo.Commodity" table="commodity"><id name="id" type="int"><column name="id" /><generator class="native" /></id><property name="name" type="java.lang.String"><column name="name" /></property><property name="price" type="double"><column name="price" /></property><property name="unit" type="java.lang.String"><column name="unit" /></property><property name="category" type="java.lang.String"><column name="category" /></property><property name="description" type="java.lang.String"><column name="description" /></property><many-to-one name="seller" class="com.imooc.vo.Seller" ><column name="seller" /></many-to-one></class>
</hibernate-mapping>
最后需要在hibernate.cfg.xml文件中配置这两个映射文件,这个步骤在这里就不再赘述了,之前的几篇笔记有介绍过很多次了。到现在我们已经完成了所有的准备工作,接下来就可以具体来分析HQL的使用了。

三、HQL的使用

1、from子句

from子句是HQL语句的最简形式,from子句指定了HQL的查询主体,与SQL的from子句指定查询某一张表不同的是HQL的from子句指定的是Java的持久化类及其属性,所以在from子句后面跟的并不是某一个表名,而是一个Java实体类名,我们来看例子,从commodity表中查询所有的商品信息,然后根据商品信息查询出对应的商家信息。我们使用JUnit来进行验证,其中SessionFactory、Session和Transaction的初始化与销毁在这里也不赘述了,与前面几篇文章里介绍的一致。
	@Testpublic void testQuery() {String hql = "from Commodity";Query query = session.createQuery(hql);List<Commodity> commoditys = query.list();for(Commodity commodity : commoditys) {System.out.println(commodity);Seller seller = commodity.getSeller();System.out.println(seller);}}
在这个例子里我们直接使用from子句作为HQL的查询子句,这是因为from子句是HQL的最简形式,我们将HQL语句作为参数传入createQuery()中创建Query实例,然后使用Query的list()方法就可以查询出所有的商品信息了,因为商品实体类中配置了与商家的多对一关系映射,所以查询到商品信息之后,就可以直接获取到商家信息了。在这个例子里我们写的HQL语句中,from子句后面没有引入Commodity类的全限定名,而是直接引入类名Commodity,当然我们也可以写全限定名,但是因为Hibernate会通过映射,自动导入缺省的全限定名。,所以写全限定名就有些多余了。
在HQL中也可以使用像SQL一样的别名,我们可以为被查询的类指定别名,在HQL语句的其他部分我们就可以通过别名引用该类了,例如上面这个例子里的HQL语句,我们可以写成这样:
String hql = "from Commodity as c";
因为这个例子只是简单地查询所有的数据,所以别名用处并不大,但是在更复杂的查询中别名的使用会有很大的用处,当然使用别名时要起容易辨识的别名,这样才能更方便地使用别名。

2、select子句

select子句是用来指定查询结果的对象和属性的,我们可以在select子句中选择返回什么类型的对象。
(1)当select子句中未指定返回的数据类型的时候,默认返回Object[]的一个List,我们看下面的例子。
	@Testpublic void testSelectClauseObjectArray() {String hql = "select s.name,s.tel,s.address from Seller s";Query query = session.createQuery(hql);List<Object[]> list = query.list();for(Object[] objs : list) {System.out.println("name=" + objs[0] + ",tel=" + objs[1] + ",address=" + objs[2]);}}
这个例子中我们需要获取name、tel和address信息,但是没有指定返回的类型,所以在调用Query.list()方法时,默认返回Object的数组类型,然后通过数组的下标获得每一个属性的值。这里需要注意的是,如果select子句只返回一个属性类型时,那么返回的类型是Object类型 而不是Object[],如果定义为Object[],系统将会报错。
	@Testpublic void testSelectClauseObjectArray() {String hql = "select s.name,s.tel,s.address from Seller s";Query query = session.createQuery(hql);List<Object[]> list = query.list();for(Object[] objs : list) {System.out.println("name=" + objs[0] + ",tel=" + objs[1] + ",address=" + objs[2]);}hql = "select s.name from Seller s";query = session.createQuery(hql);List<Object> list2 = query.list();for(Object obj : list2) {System.out.println("name=" + obj);}}
(2)我们也可以在select子句中指定返回类型为一个List的集合,这样我们就可以通过List的方法获得对象和属性了。
	@Testpublic void testSelectClauseList() {String hql = "select new list(s.name, s.tel, s.address) from Seller s";Query query = session.createQuery(hql);List<List> list = query.list();for(List l : list) {System.out.println("name=" + l.get(0) + ",tel=" + l.get(1) + ",address=" + l.get(2));}}
在这个例子中我们在select子句里使用new list(属性1,属性2,……)的方式定义了返回类型为一个List,之后我们在Query.list()方法里获得返回类型为一个List的集合,然后我们就可以通过List的get()方法获得相应的属性了。
(3)我们也可以在select子句中指定返回类型为一个Map集合,但要注意的是该Map的key为索引值,并且为字符串类型。
	@Testpublic void testSelectClauseMap() {String hql = "select new map(s.name, s.tel, s.address) from Seller s";Query query = session.createQuery(hql);List<Map> list = query.list();for(Map l : list) {System.out.println("name=" + l.get("0") + ",tel=" + l.get("1") + ",address=" + l.get("2"));}}
我们在select子句中使用new map(属性1,属性2,……)来指定返回一个Map集合,同时使用Map.get("0")的方式获得Map集合相应的value值,这里的0、1、2等是要获得的属性的索引,但是与Object[]和List不同的是,这里的索引是字符串类型的,当然我们也可以通过为属性指定别名,然后以该别名作为key值来获得相应的value值,这种方式更加清晰,可读性比较好。
	@Testpublic void testSelectClauseMap2() {String hql = "select new map(s.name as name, s.tel as tel, s.address as address) from Seller s";Query query = session.createQuery(hql);List<Map> list = query.list();for(Map m : list) {System.out.println("name=" + m.get("name") + ",tel=" + m.get("tel") + ",address=" + m.get("address"));}}
(4)我们也可以使用自定义类型返回,首先我们需要在持久化类中定义对应的构造方法,构造方法的参数需要和查询字段一致,然后在select子句中调用定义的构造方法。例如上面的例子中我们都是获取Seller的name、tel和address属性,那么我们可以在Seller类中定义相应的构造方法。
	public Seller(String name, String tel, String address) {this.name = name;this.tel = tel;this.address = address;}
然后在select子句中使用相应的构造方法,这样就可以查询到相应属性的值了。
	@Testpublic void testSelectClauseSelf() {String hql = "select new Seller(s.name, s.tel, s.address) from Seller s";Query query = session.createQuery(hql);List<Seller> list = query.list();for(Seller seller : list) {System.out.println("name=" + seller.getName() + ",tel=" + seller.getTel() + ",address=" + seller.getAddress());}}
需要注意的是,增加自定义构造方法时,一定要补充默认构造方法,否则hql=" from classname" 会出错,因为在没有指定的查询的返回集合时候,Hibernate会自动去找默认构造方法。
(5)我们也可以使用distinct关键字来过滤重复的查询结果,distinct关键字的用法和SQL的distinct关键字用法相同。
	@Testpublic void testSelectDistinct() {String hql = "select distinct s.star from Seller s";Query query = session.createQuery(hql);List<Object> list = query.list();for(Object o : list) {System.out.println(o);}}

3、where子句

where子句是用来设置查询条件,限制返回的查询结果的,在HQL中where子句的用法与SQL类似,在这里简单介绍一下。
(1)、比较运算,常用的比较运算就是=、<>、>、<、>=和<=,例如下面这个例子就是获得价格大于400的所有产品信息。
	@Testpublic void testWhere1() {String hql = "from Commodity c where c.price > 400";Query query = session.createQuery(hql);List<Commodity> list = query.list();for(Commodity c : list) {System.out.println(c);}}
在SQL中我们使用is [not] null来判断值是否为空,在HQL中我们可以直接使用=null或者<>null来进行判断,当然也可以使用SQL的is [not] null的形式来判断。
	@Testpublic void testWhere2() {String hql = "from Commodity c where c.description <> null";Query query = session.createQuery(hql);List<Commodity> list = query.list();for(Commodity c : list) {System.out.println(c);}}
(2)、范围运算,我们使用[not] in(列表)和[not] between 值1 and 值2的方式来表示范围,其中[not] in(列表)表示是否在一系列的值列表中,而[not] between 值1 and 值2表示值是否在一个范围内。
	@Testpublic void testWhere3() {String hql = "from Commodity c where c.seller not in(1,3,4)";Query query = session.createQuery(hql);List<Commodity> list = query.list();for(Commodity c : list) {System.out.println(c);}}
	@Testpublic void testWhere4() {String hql = "from Commodity c where c.seller between 1 and 3";Query query = session.createQuery(hql);List<Commodity> list = query.list();for(Commodity c : list) {System.out.println(c);}}
(3)、字符串模式匹配,我们可以使用like关键字来进行字符串的模式匹配,使用通配符%和_来进行匹配,%可以匹配任意多个字符,而_只匹配一个字符。
	@Testpublic void testWhere5() {String hql = "from Commodity c where c.category like '电_'";Query query = session.createQuery(hql);List<Commodity> list = query.list();for(Commodity c : list) {System.out.println(c);}}
	@Testpublic void testWhere6() {String hql = "from Commodity c where c.category like '电%'";Query query = session.createQuery(hql);List<Commodity> list = query.list();for(Commodity c : list) {System.out.println(c);}}
(4)、逻辑运算,在前面我们已经涉及到了逻辑非运算not,我们还有逻辑与and和逻辑或or运算。
	@Testpublic void testWhere7() {String hql = "from Commodity c where c.price>100 and c.price<5000 and c.category like '电%'";Query query = session.createQuery(hql);List<Commodity> list = query.list();for(Commodity c : list) {System.out.println(c);}}
这个例子中我们查找价格在100到5000之间,并且类型以电字开头的所有商品信息。
(5)、集合运算,在HQL中还有集合运算的概念,其中is [not] empty表示集合[不]为空,不包含任何元素,类似于SQL中的exists关键字,member of表示元素属于集合,类似于in。
(6)、四则运算,在HQL中,我们可以使用+、-、×、/四则运算,四则运算既可以在where子句中使用,也可以在select子句中使用。
	@Testpublic void testWhere8() {String hql = "from Commodity c where c.price * 5 > 3000";Query query = session.createQuery(hql);List<Commodity> list = query.list();for(Commodity c : list) {System.out.println(c);}}
(7)、查询单个对象,我们可以使用Query.uniqueResult()表示查询单个对象,也就是说uniqueResult()方法返回的只是单个的对象,如果返回结果有多个,那么这个方法就会抛出异常,我们看下面这个例子。
	@Testpublic void testWhere9() {String hql = "from Commodity c where c.name='中式童装'";//String hql = "from Commodity c where c.price > 100";Query query = session.createQuery(hql);Commodity c = (Commodity) query.uniqueResult();System.out.println(c);}
如果我们使用商品名称为中式童装来进行查询,那么最后获取到的结果就只有一个,可以使用uniqueResult(),如果我们使用商品价格大于100作为查询条件,那么会查询到多个结果,这时再使用uniqueResult()方法时就会抛出异常了。所以,在使用uniqueResult()方法时我们必须要通过where子句将查询得到的记录只限制为一条或者查询不到。

4、order by子句

类似于SQL,我们使用order by来对查询的结果进行排序,asc为升序,desc为降序,默认为升序,多个排序规则用“,”隔开,表示前一个规则中排序条件相同则用后一个排序规则。
	@Testpublic void testOrderBy() {String hql = "from Commodity c order by seller.id asc,price desc";Query query = session.createQuery(hql);List<Commodity> list = query.list();for(Commodity c : list) {System.out.println(c);}}
在这个例子中我们查询商品信息,然后对查询到的商品信息先按照卖家id进行升序排序,如果卖家id相同则按照商品价格降序排序。

















这篇关于Hibernate学习笔记(四)——HQL的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1009183

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

线性代数|机器学习-P36在图中找聚类

文章目录 1. 常见图结构2. 谱聚类 感觉后面几节课的内容跨越太大,需要补充太多的知识点,教授讲得内容跨越较大,一般一节课的内容是书本上的一章节内容,所以看视频比较吃力,需要先预习课本内容后才能够很好的理解教授讲解的知识点。 1. 常见图结构 假设我们有如下图结构: Adjacency Matrix:行和列表示的是节点的位置,A[i,j]表示的第 i 个节点和第 j 个

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件