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;
	}

	@Override
	public 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;
	}

	@Override
	public 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的初始化与销毁在这里也不赘述了,与前面几篇文章里介绍的一致。
	@Test
	public 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,我们看下面的例子。
	@Test
	public 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[],系统将会报错。
	@Test
	public 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的方法获得对象和属性了。
	@Test
	public 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为索引值,并且为字符串类型。
	@Test
	public 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值,这种方式更加清晰,可读性比较好。
	@Test
	public 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子句中使用相应的构造方法,这样就可以查询到相应属性的值了。
	@Test
	public 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关键字用法相同。
	@Test
	public 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的所有产品信息。
	@Test
	public 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的形式来判断。
	@Test
	public 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表示值是否在一个范围内。
	@Test
	public 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);
		}
	}
	@Test
	public 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关键字来进行字符串的模式匹配,使用通配符%和_来进行匹配,%可以匹配任意多个字符,而_只匹配一个字符。
	@Test
	public 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);
		}
	}
	@Test
	public 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运算。
	@Test
	public 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子句中使用。
	@Test
	public 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()方法返回的只是单个的对象,如果返回结果有多个,那么这个方法就会抛出异常,我们看下面这个例子。
	@Test
	public 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为降序,默认为升序,多个排序规则用“,”隔开,表示前一个规则中排序条件相同则用后一个排序规则。
	@Test
	public 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相同则按照商品价格降序排序。

















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值