HQL查询语言

来源:互联网 发布:税务总局网络培训学院 编辑:程序博客网 时间:2024/06/06 00:35

——HQL查询语言
 
示例:Hibernate3_10_HQL Hibernate查询:
    数据查询与检索是Hibernate中的一个亮点。相对其他ORM实现而言,Hibernate提供了灵活多样的查询机制。
标准化对象查询(Criteria Query):以对象的方式进行查询,将查询语句封装为对象操作。优点:可读性好,符合Java 程序员的编码习惯。缺点:不够成熟,不支持投影(projection)或统计函数(aggregation)
Hibernate语言查询(Hibernate Query Language,HQL):它是完全面向对象的查询语句,查询功能非常强大,具备多态、关联等特性 。Hibernate官方推荐使用HQL进行查询。 Native SQL Queries(原生SQL查询):直接使用标准SQL语言或跟特定数据库相关的SQL进行查询。 
Hibernate语言查询(Hibernate Query Language,HQL): HQL用面向对象的方式生成SQL语句:
以类和属性来代替表和数据列; 支持多态; 支持各种关联; 减少了SQL的冗余;
HQL支持所有的关系数据库操作:
连接(joins,包括Inner/outer/full joins),笛卡尔积(cartesian products); 投影(projection)--查询一到多个属性;
聚合(Aggregation,max, avg)和分组(group); 排序(Ordering); 子查询(Subqueries);
SQL函数(SQL function calls)
简单示例:查询用户名以“王”开头的所有用户。   Query query = session.createQuery(
 "from User user where user.name like '王%'");   List users = query.list();
复杂示例:从User和Group中查找属于“admin”组的所有用户。     Query query = session.createQuery(
              "from User user where user.group.name='admin'"); 如果用传统的SQL则查询语句如下:
select user.userId as userId, user.name as name, user.groupId as groupId, user.idCardId as idCardId  from TBL_USER user, TBL_GROUP group where (group.groupName='admin'  and user.groupId=group.groupId) 
HQL语言-简单属性查询: (1)单一属性查询:
   List users = session.createQuery("select name from User").list(); 说明:
    List集合中此时存储的是name属性集合,其类型为实体类(即User)的name属性的类型(本例为String类型),所以List集合中对象的类型取决于实体类中属性的类型。 
(2)多属性查询:    List users = 
        session.createQuery("select id, name from User ").list(); 说明:
    List集合中此时存储的对象数组(即Hibernate查询多个属性时返回的是对象数组,一条记录为一个对象数组,最后将所有对象数组存储到集合中),其中对象数组的长度与查询的属性个数是相同的(即select关键字后面有多少个属性,将来对象数组的长度就是多少);
    为什么会是Object类型的数组?这是因为实体类中属性的类型是不同的,所以用Object类型表示更为通用。
    数组下标元素存储的对象类型与实体类中属性的类型相同。 
(3)采用HQL动态实例化对象方式返回所要查询的属性:  List users =
  session.createQuery("select new User(id, name) from User").list(); 说明:
    List集合中此时存储的是User对象,但是该User对象并不是实体对象,而是我们自己new出来的(即不是Hibernate自动封装的),因此不受Session统一管理,此时的User对象中只有id和name属性有值,其它属性均为null。 User对象必须提供一个构造函数,形式为:     public User(int id, String name) {„}
否则将不能创建User对象,因为创建对象时调用的是带参数的构造方法。 
(4)采用别名的方式查询属性值:
    采用别名方式查询属性值,在Hibernate中可以使用as关键字指定别名,也可以不使用as关键字指定别名,两者都可以指定别名,如下所示: 不采用as关键指定别名       List users = 
     session.createQuery("select u.id, u.name from User u").list(); 采用as关键字指定别名        List users = 
         session.createQuery("select u.id, u.name from User as u").list();
说明:当指定别名后,在查询的属性名需要使用前缀时必须使用别名,而不能再使用原实体对象的名称,即: u.id, u.name。 
【小结】
单一属性查询,返回结果集属性列表,元素类型和实体类中相应的属性类型一致; 多个属性查询,返回的集合元素是对象数组,数组元素的类型和对应的属性在实体类中的类型一致;
数组的长度取决与select中属性的个数;
如果认为返回数组不够对象化,可以采用HQL动态实例化对象的方式:       select new User(id, name) from User 
HQL语言-实体对象查询: 查询实体对象
   List students = session.createQuery("from User").list(); 说明:
    此时List集合中存储的User对象为持久状态,并纳入了Session的管理,当User对象的属性发生变化时,在提交事务后会发出相应的update语句与数据库同步。 如:
if(user.getId() == 1){ user.setName("AAAA");
//会发出update语句,因为此时的user对象是实体对象,由于名字发生了变化,所以此时在提交事务后会发出相应的update语句与数据库同步 session.getTransaction().commit(); } 
使用别名查询实体对象-忽略select关键字
    采用别名方式查询属性值,在Hibernate中可以使用as关键字指定别名,也可以不使用as关键字指定别名,两者都可以指定别名,如下所示: 不采用as关键指定别名
    List users = session.createQuery("from User u").list(); 采用as关键字指定别名
    List users = session.createQuery("from User as u").list(); 
如:
List students = session.createQuery("select u.id, u.name from User u where CONVERT(varchar(10), DATEPART(mm,u.create_time))=?")    .setParameter(0, "06")             .list(); 
(6) 查询某一时间段的数据
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //查询2010-06-10到2010-07-31入职的员工
List users = session.createQuery("select u.id, u.name from User u where u.create_time between ? and ?")
.setParameter(0, sdf.parse("2010-06-10 00:00:00")) .setParameter(1, sdf.parse("2010-07-31 23:59:59")) .list();
说明:在实际项目开发中,关于日期段的查询一般都是从起始日期的00:00:00时间开始,结束于结束日期的23:59:59,这样对于日期类型的数据库表的字段更为精确。 
【小结】
可以采用拼字符串的方式传递参数
可以采用 ?来传递参数(索引从0开始) 可以采用 :参数名 来传递参数
如果传递多个参数,可以采用setParamterList方法
在HQL语言中可以使用数据库的函数,如:CONVERT、DATEPART 
HQL语言-原生SQL语句查询: 采用原生SQL语句查询
List users = session.createSQLQuery("select * from t_user").list(); 说明:
使用原生SQL语句,需要使用:SQLQuery接口; 使用原生SQL语句时,不会往Session(一级)缓存中放入数据,即SQLQuery接口中的list()方法不会往Session(一级)缓存中放入数据;
SQLQuery接口的list()方法返回的List集合中存储的是数组对象 
HQL语言-外置命名查询:
    外置命名查询就是指把HQL语句放到一个配置文件中,而不是把HQL语句直接写到程序代码中,采用这种方式的目的是为了让HQL语句与程序代码更好的进行解耦。
在实际开发中这并不是决对的,因为当把所有的HQL语句放入到一个配置文件中在调试时,会增加一定的工作量,有时显得会不太方便,当项目追求低耦合度时,可以考虑采用这种方式。 
实体映射文件:
在</class>的标签后面加入如下代码: <!-- 外置命名查询定义 --> <query name="searchUsers">
    <![CDATA[
                SELECT u FROM User u where u.id<?      ]]> </query>
说明: 配置文件中之所以使用<![CDATA[„ ]]>是因为在HQL语句中含有”<”号,该符合是XML语言的组成部分,如果文本中含有XML语言特定的符号时可以将文本放在<![CDATA[„ ]]> 中,如果不使用<![CDATA[„ ]]>,则这些特定符号需要进行转义,否则会引起一些问题。 
使用:
List users = session.getNamedQuery("searchUsers")
.setParameter(0, 6) .list(); 说明:
    searchUsers:为配置文件中外置命名查询的名称
    setParameter(0, 6):为外置命名查询的HQL语句的占位符赋值 在映射文件中采用<query>标签来定义HQL语句
在程序中采用session.getNamedQuery()方法得到HQL查询串 
HQL语言-查询过滤器:
启用查询过滤器后,当Session没有关闭的时候,只要在Session中执行的所有HQL语句都会自动加上查询过滤器中的条件(即实现过滤功能)。 
(1)定义查询过滤器
在</class>的标签后面加入如下代码:  <filter-def name="filtertest">
  <filter-param name="myid" type="integer"/>  </filter-def> 说明: 
filtertest:查询过滤器名称 myid:过滤条件中的参数名称
type:指定参数的类型,本例为小写的integer表示这是Hibernate中的类型,也可以写成Java中的java.lang.Integer 
(2)为实体映射文件指定使用查询过滤器 在<class>标签中的最后加入如下代码:
    <!-- 指定使用查询过滤器,i_id数据库字段名 -->
    <filter name="filtertest" condition="i_id &lt; :myid"/> 说明:
name:指定要使用的查询过滤器名称 condition:指定查询过滤器的条件,将来过滤器将使用该条件进行过滤操作。其中“&lt; ”表示的是“<”号,因为该符号是XML语言的特殊符号,所以在这里需要进行转义。 
(3)在程序中启动查询过滤器
//启用查询过滤器,只有启动以后查询过滤器才起作用 session.enableFilter("filtertest")     .setParameter("myid", 6); 【小结】
在映射文件中定义过滤器参数 在类的映射中使用这些参数 在程序中启用过滤器
查询过滤器中的条件在Hibernate生成具体的SQL语句时会自动加上该条件,所以条件中必须使用表中的字段名 
HQL语言-分页查询: Hibernate分页查询
List users = session.createQuery("from User")
//指定从第几条记录开始,下标从0开始,本例为从第2条记录开始查询
.setFirstResult(1) //每页显示多少条记录
.setMaxResults(2) .list(); 
HQL语言-对象导航查询:
    所谓的对象导航查询是指查询某一个实体时,条件是这个实体所关联的另外一个实体对象,例如:查询员工的姓名,条件是根据部门名称进行查询。 对象导航查询: List users = 
session.createQuery("select u.name from User u where u.dept.name like '%人力%'").list();
HQL语言的含义是:查找部门名称中含有“人力”字样的部门中的员工姓名字段。 说明:查询的条件是根据User类中的关联的实体对象dept的名称来进行查询的,其中dept是User类中对Dept的引用,那么这种导航的工作原理是根据配置文件中的关联映射完成的。 在HQL语句中采用 . 进行导航 
HQL语言-连接查询:
Hibernate中支持3种连接查询: 内连接 左外连接 右外连接
说明: HQL中的连接必须采用对象导航进行连接,否则将抛出 org.hibernate.hql.ast.QuerySyntaxException异常。

(1)内连接

使用inner关键字

List users = session.createQuery("select d.name, u.name from User u inner join u.dept d").list();

说明:  在Hibernate中上述 的HQL语句就能够将User实体对象和Dept实体对象的关联关系建立起来了,因为这都是通过实体映射文件来完成的,体现在表上则是通过两张表的连接完成的。

(2)左外连接

示例:查询部门的名称(包含没有员工的部门)及员工的姓名
List users = session.createQuery("select d.name, u.name from Dept d left join d.users u").list();

说明:  Hibernate会根据Dept实体类的映射文件中的关联映射关系自动发出左外连接的SQL语句(即t_dept表与t_user表之间采用左外连接的SQL语句)。

(3)右外连接

示例:查询员工姓名(包含未分配部门的员工)及员工所在部门的名称。
List users = session.createQuery("select d.name, u.name from Dept d right join d.users u").list(); 说明:Hibernate会根据Dept实体类的映射文件中的关联映射关系自动发出右外连接的SQL语句(即t_dept表与t_user表之间采用右外连接的SQL语句)。 
HQL语言-统计查询:
    Hibernate 3.x支持统计查询,但是使用Hibernate做纯粹的统计应用比较少,因为涉及到的数据量非常大,在实际项目开发中,一般采用第三方的报表工具,比较常用的有:iReport、如意报表、JfreeChart等。 
示例:查询员工人数
统计总数,一般有编程两种方式:
(1)采用获取返回的List集合长度的方式
         List users = session.createQuery("select count(id) from User").list();           //Hibernate的统计返回值一般都是long型,防止统计数据过大造成越界异常           Long count = (Long)users.get(0);
说明:这种方式效率不高,因为返回的是一个List,而该List中就一个元素(即统计的数量值),然后还需要使用get方法将该元素取出。 (2)采用从返回的Long对象中获取统计数值 Long count = 
     (Long)session.createQuery("select count(id) from User").uniqueResult(); 说明:这种方式效率高,因为返回的不是一个List集合,而是一个长整型的对象,该对象即是统计出来的数据,因为Hibernate使用长整型来封装统计出来的数值,目的是防止统计出来的数值过大,造成数据溢出异常。数据类型一定要切记,是Long(即封装类)。 
HQL语言-DML查询:     Hibernate 3.x支持DML风格的操作,所谓DML风格就是指批量的执行insert、update、delete等操作这里的这些操作指的都是批量执行Hibernate中对应的save、update、delete方法。
    但是这些批量操作一般不建议使用,因为当批量执行一批语句后,Hibernate是不会修改缓存的,这就造成了数据库中的数据与缓存中的数据不同步的问题,假设一次性批量插入1万条数据,这时Hibernate不可能将这1万条数据都放入缓存,因为这非常容易导致内存溢出异常。
    在实际项目开发中对于那些变化不大数据( 即不是经常访问的数据)可以采用DML来操作。 示例:
session.createQuery("update User u set u.name=? where u.id < ?")
.setParameter(0, "XXX") .setParameter(1, 5) .executeUpdate();
说明:执行完批量的update操作后数据库中的值已经发生了变化,但是缓存中的值并没有发生变化还是原来的值,但是当我们访问时,其实访问的是缓存中的值,这就造成了数据的不准确性。 
注:
session.get()和session.load()区别: get()方法:
1. 不支持延迟加载,会马上发出查询sql语句,加载User对象;
2. 使用get()方法加载数据,如果数据库中不存在相应的数据,则返回null。 load()方法:
1. 支持延迟加载,只有真正使用这个对象的时候,才会发出查询sql语句
2. 使用load加载数据,如果数据库中没有相应的数据,那么抛出ObjectNotFoundException,即不会返回null。
get(),load()方法会把查询上来的数据放入缓存,也会使用缓存
get(),load()方法会将查询上来的数据放入一级缓存中一份,然后再将该数据放入二级缓存中一份;先查找一级缓存再查找二级缓存,如果在任何一级缓存中能够查到该数据,则直接进行使用,否则发出SQL加载数据
结论:session是共享二级缓存(SessionFactory缓存)的 
Query query = session.createQuery("") query.iterate()
iterate() 查询实体对象: iterate() 使用缓存(但不包括查询缓存,oid是存储在查询缓存中的),发出查询id的SQL语句,不会发出查询实体对象的SQL语句。
原理同load()方法,缓存中如果存在该实体对象,则直接使用,如果不存在该实体对象,则发出SQL语句去查询。 
iterate() 方法查询普通属性:
一级缓存、二级缓存只存储实体对象
发出查询姓名属性的SQL语句;不会发出查询ID属性列表的SQL语句(因为此时的HQL语句查询的是普通属性而不是实体对象)
* 一(二)级缓存是缓存实体对象的,即如果查询出来的是实体对象,则才向一(二)级缓存中存放,如果查询出来的是普通属性,则不向一(二)级缓存中存放。
重点!!!
* 思考:执行如下语句,是否将查询出来的数据存放到一级缓存中?
 Iterator iter = session.createQuery("select new User(u.name) from User u where u.id=1").iterate();
  答案:不会向一级缓存中存放,因为此时查询出来的User对象是我们自己new出来的,而不是Hibernate自动为我们new出来的,所以不受Session管理。 
Query.list()方法:
1、当关闭查询缓存(默认)时,list()方法不使用一级缓存和二级缓存中的数据
2、当开启查询缓存时,list()方法就会使用一级缓存和二级缓存中的数据,顺序是先找一级缓存再找二级缓存 
Session 、SessionFactory:
clear() 方法清除缓存中的全部实体对象
evict() 方法可以指定要从缓存中清除哪个实体对象
0 0
原创粉丝点击