第9讲 Hibernate查询语言HQL
回顾 单边一对多关系 单边多对一关系 双边多对一、一对多关系 单边多对多关系 双边多对多有关系 单边一对一关系 双边一对一关系 如何使用MyEclipse生成各类关系
本讲问题 HQL的含义 HQL查询与SQL查询的区别 HQL查询的返回类型 HQL查询如何返回多个对象 HQL中如何使用统计函数 Hibernate中如何使用SQL查询和命名查询
本讲任务 更新选课系统 制作IDepartmentDao与DepartmentDaoImpl类 制作IStudentDao与StudentDaoImpl类 制作students.jsp显示所有的学生信息(many-to-one的lazy=false) 制作学生的分页显示pagestudents.jsp 制作通过系院查询学生信息的depstudents.jsp
本讲目标 了解HQL的语法和基础查询 掌握查询的返回类型 掌握同时返回多个对象的方法 掌握使用查询条件的方法 掌握统计函数的使用方法 掌握Hibernate中使用SQL查询及命名查询的方法
为什么使用HQL HQL(Hibernate Query Language)Hibernate查询语言,语法类似于SQL,可以直接使用实体类及属性。 使用HQL 可以避免使用JDBC 查询的一些弊端 不需要再编写繁复的SQL 语句,针对实体类及其属性进行查询 查询结果是直接存放在List 中的对象,不需要再次封装 独立于数据库,对不同的数据库根据Hibernate dialect 属性的配置自动生成不同的SQL 语句执行 [select/update/delete……] from Entity [where……] [group by……] [having……] [order by……]
HQL基础查询 HQL语法类似于SQL,有SQL的关键词select、from、order by、count()、where等。 所用的小猫Cat实体的属性: id微机编码,name名字,description描述,mother妈妈 小猫的简历event实体的属性: id微机编码,catId小猫的id,nr简历内容
HQL基础查询--HQL语法 HQL语法类似于SQL,是一种select ... from结构。其中,from后跟的是实体类名,而不是表名。select后面跟的可以是实体对象,也可以是实体对象的属性或者其他的值。如: Query query = session.createQuery("select c from Cat as c"); //查询所有的Cat List<Cat> catList = query.list(); //执行查询,返回List 其中Cat为实体类Cat,c为Cat对象,关键词as用法同SQL,可以省略。Query是Hibernate的查询对象,query.list()将以List类型返回查询结果,上面代码也可以简写为: Query query = session.createQuery("from Cat"); //查询所有的Cat
HQL基础查询--使用Package名 如果只有一个名为Cat的实体类,Cat类的package可以省略。Hibernate会根据类名检索到实际的Cat实体类。 但如果有两个Cat类,查询时必须携带package信息,否则Hibernate无法知道是哪个Cat实体类。如: select c from org.sf.entity.Cat c。
HQL基础查询--HQL大小写不敏感 HQL语言大小写不敏感,select c from Cat也可以写作SELECT c FROM Cat。 但是涉及Java类名,package名,属性名时,大小写敏感,因此Cat必须与类名一致。
返回类型 Hiberante使用Query对象进行查询。Session的createQuery()方法能够创建Query实例,参数为HQL。Query对象能够返回各种类型的查询结构,例如:long、、String、List<实体类>、List<Map>、POJO等。 最常见的查询方法有uniqueResult()与list()等。其中uniqueResult()返回单个值,而list()返回零个或多个值。
返回类型--查询单个对象 Query的unique()返回单个对象。使用unique()获取返回值时,HQL语句查询到的结果最多只能有一个,如果结果多于一个,unique()方法会抛出异常。如果没有,会返回null。这个方法常常用来查询记录总数,因为总会返回一个对象,并且也只有一个。 如: Query q =session.createQuery("select count(c) from Cat c"); //创建查询对象 Number num = (Number)q.uniqueResult(); //返回单个实例 int count = num.inValue(); //返回数值
返回类型--返回集合属性 Query的list()方法是最常用的方法。实际上,unique()方法也是在list()方法得到返回数据后执行的。list()总是返回一个java.util.List对象,里面有0个或多个值。list()可以返回实体对象,也可以返回实体对象的某个属性或某些属性。 如: List<Cat> catList = session.createQuery("select c from Cat c").list(); List<String> nameList = session.createQuery("select c.name from Cat c").list(); List<String[]> nameList=session.createQuery("select c.name, c.mother.name from Cat c").list(); List<Cat> motherList = session.createQuery("select c.mother from Cat c"); 返回存储Cat对象的List 返回存储猫名字(String类型)的List 返回猫名字、猫妈妈名字的字符串数组(String[]类型)的List 返回猫妈妈的List,虽然是Cat的一个属性,但仍然是Cat类型
同时返回多个对象 Query的list()方法返回java.util.List对象。List中一般存储完整的实体类对象。如select c from Cat c,会将所有的Cat都查询出来,包含Cat类所有即时加载的属性。 对于有些查询,只需要查几个属性就可以了,这时候可以在HQL中指定要返回的部分。查询部分属性时,返回结果仍然是List类型,里面可能是单个的Object,也可能是Object[]数组,还可能是List对象或Map对象。返回什么类型数据,由HQL语句决定。
同时返回多个对象--返回Object[]数组 查询多个属性时,Hibernate将同时返回多个对象(以Object[]对象类型返回)。返回的数组是放到List<Object[]>中的,得到返回数组需要遍历List对象。 如: List<Object[]> list = session.createQuery("select c.name,c.mother.name, c.createDate from Cat c").list(); //返回list for(Object[] row:list){ //遍历第1层的list for(Object obj:row){ //遍历第2层的数组 System.out.println(""+obj); //输出Object } 查询多个属性
同时返回多个对象--返回List类型 返回结果还可以放到List<List>中。查询时HQL采用“select new List(a,b,c) from ...”的形式。同样需要遍历List来获取返回的List,再遍历返回的List获取查询结果。 如: List<List> list = session.createQuery("select new List( c.name,c.mother, c.createDate) from Cat c").list(); //关键词List for(List row:list){ //遍历第1层的list for(Object obj:row){ //遍历第2层的数组 System.out.println(""+obj); //输出Object }
同时返回多个对象-- 返回Map类型 更实用的是返回Map类型。Map中将包含查询的列名、值。遍历List<Map>获得Map,从Map中直接取值就可以了,或者遍历Map。 如: List listMap = session.createQuery("select new Map( c.name as name,c.mother as mother, c.createDate as createDate) from Cat c").list(); //关键词List for(Map map:(List<Map>)listMap)){ //遍历第1层的list System.out.println("name:"+map.get("name")); System.out.println("Mother:"+map.get("mother")); System.out.println("CreateDate:"+map.get("createDate")); } 输出Map属性 输出Map属性 输出Map属性
同时返回多个对象--返回Java实体对象 对于只查询部分属性的查询,返回数组、List、Map时,很方便,但是操作Object[]数组、List、Map等不如操作实体对象方便。实际上查询部分属性时,也可以返回实体对象。HQL中也可以使用构造函数。如: List<Cat> catList=session.createQuery("select new Cat(cat.name, cat.createDate) from Cat c").list(); 这样使用时,Cat类必须存在 一个public Cat(String name, Date createDate)的构造函数。因为Hibernate是通过该构造函数完成返回值从Object[]数组转化到Cat实体类的。
条件查询 HQL用where连接条件子句,语法类似于SQL。大部分SQL的规则对于HQL都适用,例如等于(=)、大于(>)、小于(<)等。
条件查询--where子句语法 HQL的where子句中使用的是实体类的属性,或者是属性的属性。查询条件可以写在HQL中,也可以通过Query设置参数。 如: session.createQuery("select c from Cat c where c.mother.name = null and c.createDate<:createDate") .setParameter("createDate", new Date()).list();
条件查询--HQL支持的运算符 HQL的where子句中可以使用的运算符包括: 数学运算符:+、-、*、/。 比较操作符:=、!=、<>、>=、<=、>、<、like。 逻辑运算符:and、or、not。 SQL操作符:in、not in、between、is null、is not null、is empty、number of等。 字符串连接符:...||...或者concat(..., ...)。 时间日期函数:current_date()、current_time()、current_teimstamp()、second()、minute()、hour()、month()、year()等。 JPA定义的操作:substring()、trim()、lower()、upper()、length()、locate()、abs()、sqrt()、bit_length()、coalesce()、nullif()。
条件查询--HQL支持的运算符 数据库支持的SQL标题函数:sign()、trunc()、rtrim()、sin()。 简单的跳转语句:case...when...then...else...end。 如: List<String> sList = session.createQuery("select c.name || '的妈妈为' || c.mother.name from Cat c where c.mother!=null").list(); LIst<Cat> cList = session.createtQuery("from Cat c where lower(trim(c.name) in ('hello kitty', 'ricky', 'micky'))").list(); List<Cat> cList = session.createQuery("from Cat c where size(c.events)>5").list(); 字符串连接 in子句查询 集合查询
统计函数 跟SQL一样,Hibernate也提供一系列的统计函数。Hibernate会把HQL的统计函数转化为底层数据库SQL支持的函数。 SQL里面常用的统计函数如count()、sum()、min()、max()、avg()、count(distinct ...)等也可以用在HSQL中,语法与SQL一样。如: Number num = (Number)session.createQuery("select count(c) from Cat c where c.mother!=null").uniqueResult(); int count = num.intValue(); 查询总数时并不是返回Long类型。返回值类型由实体类的主键类型决定的。如果实体类的主键为short类型,则返回值可能为Integeger类型。
演示使用Hibernate实现分页的页面程序 HQL分页显示 分页显示是Web数据库程序必备的功能。不同的数据库使用不同的方式实现分页,例如MySQL使用limit,oracle使用rownum。Hibernate隐藏了所有的细节,只需要设置当前页数即可。 分页显示一般先查询记录总数,然后查询本页显示的记录。Hibernate通过Query查询记录,Query通过setFirstResut()设置分页的第一条记录,通过setMaxResults()设置本页的数据,如: long count=(Long)session.createQuery("select count(c) from Cat c").uniqueResult(); List<Cat> ccList = session.createQuery("from Cat").setFirstResult(0).setMaxResults(10).list(); 演示使用Hibernate实现分页的页面程序
HQL级联查询 Hibernate能够管理具有复杂关系的实体,当然也提供了复杂的查询支持。 对于一般的跨表查询,只需要简单地使用属性就可以了。
HQL级联查询--跨表查询 对于 一般的跨表查询,表连接就足够了。Hibernate支持用"."作为操作符获取属性,用法类似于JSP的EL表达式。表连接查询适用于非集合属性,例如Event的cat属性,Student的department属性等。如: List<Event> eventList=session.createQuery("select s from Student s where s.department.mc='jsj'").list(); 表面上该查询只涉及到了Student表,但实际上因为where子条件用到了s.department.mc='jsj',将会查询Student与Deparment对象。
HQL级联查询--级联查询 有些查询需要使用级联查询。HQL支持SQL的级联查询,包括inner join、left join、right join、full join等。级联查询适用于集合属性等。例如查询students集合属性中含有”王”的院系信息,查询语句为: List<Department> depList = session.createQuery(“select d from Department d left join d.students s where s.stuname like :stuname”).setParameter(“stuname”, “%王%”).list();
Hibernate中使用SQL查询 HQL可以看作是对所有SQL数据库的封装。HQL提供的功能是底层数据库SQL支持的,HQL只是将功能“翻译”成了底层的SQL的功能。有些情况下,底层数据库会提供某种功能,但是可能HQL不支持。这时可以使用底层SQL,在专业术语 叫做本地SQL(Native SQL)。
Hibernate中使用SQL查询--使用SQL Query 使用SQL数据库查询时不能使用Query,而要使用SQLQuery对象。 如在MySQL数据库中查询所有的变量: SQLQuery sqlQuery = session.createSQLQuery(“show variables”); List<Object[]> list = sqlQuery.list(); for(Object[] obj:list){ System.out.println(obj[0]+”,”+obj[1]+”,”); } 使用本地SQL 执行查询 遍历所有的属性 输出属性值
Hibernate中使用SQL查询--返回实体类型 SQLQuery与Query一样,都可以设置参数、分页显示等。SQLQuery返回的结果为List<Object[]>类型。也可以设置为实体类,使查询结果直接返回实体类对象。 如: SQLQuery sqlQuery = session.createQuery(“select * from student”); sqlQuery.addEntity(Student.class); List<Student> stuList = sqlQuery.list(); 如果设置的实体在与查询结果不一样,会抛出异常。
Hibernate命名查询 有些查询是常用的。Hibernate中可以命名常用的查询,需要使用的是只需要引用名称就可以了。命名查询一般配置在实体类中。
Hibernate命名查询--@配置命名查询 使用@注解配置实体类时,要使用@注解配置命名查询,用到的Java注解为@NamedQuery与@NamedNativeQuery。其中,@NamedQuery用于配置命名的HQL查询,@NamedNativeQuery用于配置命名 底层数据库SQL查询。 例如: import javax.persitence.*; @NamedQuery(name=“all cat”, query=“select c from Cat c”) @NamedNativeQuery(name=“all cat”, query=“select * from tb_cat”) @Entity @Table(name=“tb_cat”) public class Cat{ … } 命名查询 命名本地查询 Entity配置 Table配置
Hibernate命名查询—设置查询扩展 命名查询中,允许使用@QueryHint对命名查询设置JPA扩展。JPA规范允许JPA供应商(如Hibernate、Toplink、OpenJPA等ORM框架)对JPA进行一些功能上的扩展,以加速查询性能、提供其他功能等。 如: @NamedQuery(name=“cat by name”, query=“select c from Cat c where c.name=:name”, hints={@QueryHint(name=“org.hibernate.callable”, value=“true”)})
Hibernate命名查询—设置多个命名查询 一个实体类不能配置多个@NamedQuery。如果有多个命名查询,需要使用@NamedQueries配置。@Named- Queries中可以配置多个@NamedQuery,例如: @NamedQueries(value={ @NamedQuery(name=“all cat”, query=“select c from Cat c”), @NamedQuery(name=“cat by name”, query=“select c from Cat c where c.name=:name”), @NamedQuery(name=“cat by mother”, query=“select c from Cat c where c.mother=:mother”) }) 程序中按如下方法使用命名查询,如果有参数,需要设置参数: Query q = session.getNamedQuery(“Cat by name”).setParameter(“name”, “kitty”); List<Cat> catList = q.list();
Hibernate命名查询—XML配置命名查询 如果实体类使用XML配置的,命名查询需要配置在hbm.xml实体类配置文件中。命名查询使用<sql-query>配置,要配置在实体类的后面,name配置查询的名称,<return/>可配置返回数据的类型。 如: <sql-query name="cat by name"> <!-- 配置命名查询 --> <![CDATA[ select c from Cat c where c.name=:name ]]> <return alias="" class="org.sf.entity.Cat" /> </sql-query> 查看TbCat.hbm.xml
制作IDepartmentDao与DepartmentDaoImpl类 制作IStudentDao与StudentDaoImpl类 更新学生选课系统 制作IDepartmentDao与DepartmentDaoImpl类 制作IStudentDao与StudentDaoImpl类 制作students.jsp显示所有的学生信息(many-to-one的lazy=false) 制作学生的分页显示pagestudents.jsp 制作通过系院查询学生信息的depstudents.jsp 演示各个程序及功能
本讲目标 了解HQL的语法和基础查询 掌握查询的返回类型 掌握同时返回多个对象的方法 掌握使用查询条件的方法 掌握统计函数的使用方法 掌握Hibernate中使用SQL查询及命名查询的方法