Java Hibernate中使用HQL语句进行数据库查询的要点解析

一、实体对象查询

实体对象查询是hql查询的基础,作为一种对象查询语言,在查询操作时和sql不同,查询字符串中的内容要使用类名和类的属性名来代替。这种查询方法相对简单,只要有SQL功底,使用hql是很简单的,但是有一些问题需要注意,就是查询获取数据不是目的,需要考虑的是如何编写出高效的查询语句,这才是讨论的重点。

1.N+1问题

(1)什么是N+1问题
在刚听到这个名词时疑惑可能是有的,以前根本就没有听过N+1问题,那么它是指什么呢?N+1指的是一张表中有N条数据,那么在获取这N条数据时会产生N+1条sql命令,这种操作被称为N+1。这里的1指的是发出一条查询id列表的语句,N则指根据id发出N条sql语句,加载相关的对象。这种查询操作效率很低,往往产生在迭代器中,也就是说如果我们将查询结果直接转化为迭代器,这时候就会出现这种问题,如下代码:

public void testQuery(){
  Session session=null; 

  try{
    session=HibernateUtils.getSession();
    session.beginTransaction(); 

    /**
     * 会出现N+1问题,所谓的N+1值得是发出了N+1条sql语句
     *
     * 1:发出一条查询id列表的语句
     *
     * N:根据id发出N条sql语句,加载相关的对象
     */
    Iterator iter=session.createQuery("from Student").iterate(); 

    while(iter.hasNext()){
      Student student=(Student)iter.next();
      System.out.println(student.getName());
    }
    session.getTransaction().commit();
  }catch(Exception e){
    e.printStackTrace();
    session.getTransaction().rollback();
  }finally{
    HibernateUtils.closeSession(session);
  }
}

上面的这段查询代码就会产生N+1问题,因为查询时返回的是一个迭代器,这样没产生一次就会发出一条sql语句,这主要取决于Iterator的这种查询机制,它是从缓存中查询数据,如果缓存中不存在该数据那么首先会将数据转换到内存中,所以这时候就会发出一条sql查询语句,所以在每次迭代时就会产生一条sql语句。这种写法其实是一种错误,可以使用其它方法优化解决。

(2)避免N+1问题
出现了N+1的问题是因为Iterate使用不当的原因,当然可以使用其它的方法来避免这种N+1的问题,这里介绍一种List的方法。List和Iterate最大的区别是List将数据放到缓存中,但是不利用缓存,默认情况下list每次都会发出sql语句。可以在使用Iterate之前,使用list将数据保存到数据库中,这样Iterate访问数据时就可以从缓存中读取,避免了N+1问题的出现,代码如下:

public void testQuery(){
  Session session=null; 

  try{
    session=HibernateUtils.getSession();
    session.beginTransaction(); 

    List students=session.createQuery("from Student").list(); 

    System.out.println("---------------------------------");
    /**
     * 避免了N+1问题
     *
     * 因为执行list操作后会将数据放到session的缓存中(一级缓存),所以采用iterate的时候
     * 首先会发出一条查询id列表的语句,再根据id到缓存中加载相应的数据,如果缓存中存在与之匹配的数据
     * 则不再发出根据id查询的sql语句,直接使用缓存中的数据
     *
     * Iterate方法如果缓存中存在数据,它可以提高性能,否则出现N+1问题
     */
    //可以使用as别名
    Iterator iter=session.createQuery("from Student").iterate(); 

    while(iter.hasNext()){
      Student student=(Student)iter.next();
      System.out.println(student.getName());
    }
    session.getTransaction().commit();
  }catch(Exception e){
    e.printStackTrace();
    session.getTransaction().rollback();
  }finally{
    HibernateUtils.closeSession(session);
  }
}

上例 避免了N+1问题,因为执行list操作后会将数据放到session的缓存中(一级缓存),所以采用iterate的时候首先会发出一条查询id列表的语句,再根据id到缓存中加载相应的数据,如果缓存中存在与之匹配的数据, 则不再发出根据id查询的sql语句,直接使用缓存中的数据。 Iterate方法如果缓存中存在数据,它可以提高性能,否则出现N+1问题。

2.对象导航查询

对象导航是指在一个对象中按照对象的属性导航获取到另一个对象的数据,这样做可以简化查询语句,优化查询方法。如果按照我们平常的想法可能会再重写编写另一个类的对象,来获取另一个对象的操作,和对象导航对比语句比较累赘。

@SuppressWarnings({ "unchecked", "rawtypes" })
public void testQuery1(){
  Session session=null; 

  try{
    session=HibernateUtils.getSession();
    session.beginTransaction(); 

    //返回结果集属性列表,元素类型和实体类中的属性类型一致
    List students=session.createQuery("from Student s where s.classes.name like '%2%'").list(); 

    for(Iterator ite=students.iterator();ite.hasNext();){
      Student obj=(Student)ite.next();
      System.out.println(obj.getName());
    } 

    session.getTransaction().commit();
  }catch(Exception e){
    e.printStackTrace();
    session.getTransaction().rollback();
  }finally{
    HibernateUtils.closeSession(session);
  }
}

上例中的查询语句就使用了对象导航的方法,查询语句是从Student对象中查询的信息,但是要比对的对象属性却是来自于Classes对象的name属性,这时候使用对象导航的查询方法就会明显提高查询效率,优化查询语句,如果换做普通的查询方法就可能会产生大量的连接语句,很复杂。

二、sql原生查询

原生查询值的是使用SQL语句来查询获取数据,而不是采用hql语句,它的使用方法其实很简单,同hql类似,只需要使用createSQLQuery方法查询即可,它其实类似于hql的createQuery方法,代码如下:

public void testQeury(){
  Session session=null;
  try{
    session=HibernateUtils.getSession();
    session.beginTransaction(); 

    List list=session.createSQLQuery("select * from t_student").list(); 

    for(Iterator ite=list.iterator();ite.hasNext();){
      Object[] obj=(Object[])ite.next();
      System.out.println(obj[0]+","+obj[1]);
    } 

    session.getTransaction().commit();
  }catch(Exception e){
    e.printStackTrace();
    session.getTransaction().rollback();
  }finally{
    HibernateUtils.closeSession(session);
  }
}

上面的代码使用了createSQLQuery方法,方法内的查询字符串就是SQL语句,它实现了底层的字符串查询方法,不同的是HQL又做了一层包装,在Hibernate.cfg.xml中配置相应的方言选项即可完成映射。

三、连接查询

在sql中经常使用连接查询来获取多个对象的合集,其中经常用到的有inner join、left join、right join等,分别指代内连接查询、左外连接查询、右外连接查询,它们在查询时返回的内容分别是实体之间的笛卡尔积,查询的内容及左表的一些内容,查询内容及右表的一些内容,查询的功能强大。hql的连接查询方法和sql的连接查询在查询结果上是相同的,但是在查询语句上稍有区别。

1.内连接

hql的内连接查询可使用inner join语句或者join语句查询,获取的结果集是笛卡尔积。同sql的内连接查询类似,hql的join查询又分为显式与隐式两种,显示的查询是指查询字符串中有join关键字,隐式的查询在字符串中不需要添加join。

//内连接
@SuppressWarnings({ "unchecked", "rawtypes" })
public void testQuery(){
  Session session=null; 

  try{
    session=HibernateUtils.getSession();
    session.beginTransaction(); 

    //返回结果集属性列表,元素类型和实体类中的属性类型一致
    List students=session.createQuery("select s.name,c.name from Student s join s.classes c").list(); 

    for(Iterator ite=students.iterator();ite.hasNext();){
      Object[] obj=(Object[])ite.next();
      System.out.println(obj[0]);
    } 

    session.getTransaction().commit();
  }catch(Exception e){
    e.printStackTrace();
    session.getTransaction().rollback();
  }finally{
    HibernateUtils.closeSession(session);
  }
}

2.外连接
外连接又分为左外连接和右外连接查询,查询方法类似,但是查询出的结果集不同,它们在查询结果上和SQL的外连接相同,不同的是写法,具体使用代码如下:

@SuppressWarnings({ "unchecked", "rawtypes" })
public void testQuery(){
  Session session=null; 

  try{
    session=HibernateUtils.getSession();
    session.beginTransaction(); 

    //返回结果集属性列表,元素类型和实体类中的属性类型一致
    List students=session.createQuery("select s.name,c.name from Student s left join s.classes c").list(); 

    for(Iterator ite=students.iterator();ite.hasNext();){
      Object[] obj=(Object[])ite.next();
      System.out.println(obj[0]);
    } 

    session.getTransaction().commit();
  }catch(Exception e){
    e.printStackTrace();
    session.getTransaction().rollback();
  }finally{
    HibernateUtils.closeSession(session);
  }
}

上面的代码使用的是左外连接查询语句,相应的右外连接查询的方法和左外连接类似,将left转换为right即可。查询到的数据被保存到list对象中,可通过list来获取查询内容。

四、外置命名查询

外置命名查询是指将查询语句写到映射文件中,在映射文件中使用<query>标签来定义hql语句,这样定义的hql语句就能够实现功能配置功能,如果出现问题只需要修改配置即可。如果想用使用该sql语句,可在程序中使用session.getNamedQuery()方法得到hql查询串,如下示例。

1.外置查询语句
下面示例中演示了外置查询语句的应用,在映射文件中添加<query>标签,并为该标签添加name属性,将字符串添加到<![CDATA[]]>中,这样既可在程序中按照query的name属性获取对应的查询字符串了。

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
  <class name="com.src.hibernate.Student" table="t_student">
    <id name="id">
      <generator class="native"/>
    </id>
    <property name="name"/>
    <property name="createTime"></property>
    <!-- 在多的一端Student中添加一行新的Classes列 ,并且列的名称要和Classes.hbm.xml的列明相同-->
    <many-to-one name="classes" column="classesid"></many-to-one>
  </class> 

  <query name="queryStudent">
    <![CDATA[
      select s from Student s where s.id<?
    ]]>
  </query> 

</hibernate-mapping>

外置的命名查询将查询语句放到了映射文件中,所以它可以被认为是公共的查询字符串,在程序文件中都可以查询使用该字符串,这是它的优点,这样其它的程序文件就都可以获取使用了,另外作为公共的字符串增加了修改的便利性。

2.程序应用

定义了外置的查询语句后要在程序中使用,hql提供了getNameQuery方法来获取外置的查询语句串,该方法中需要添加一个外置的游标名称,hql会根据游标名称查询获取相对应的sql语句块,如下的程序代码:

//外置命名查询
@SuppressWarnings({ "unchecked", "rawtypes" })
public void testQuery(){
  Session session=null; 

  try{
    session=HibernateUtils.getSession();
    session.beginTransaction(); 

    List students=session.getNamedQuery("queryStudent").setParameter(0, 10).list(); 

    for(Iterator ite=students.iterator();ite.hasNext();){
      Student obj=(Student)ite.next();
      System.out.println(obj.getName());
    } 

    session.getTransaction().commit();
  }catch(Exception e){
    e.printStackTrace();
    session.getTransaction().rollback();
  }finally{
    HibernateUtils.closeSession(session);
  }
}
(0)

相关推荐

  • Java的Hibernate框架结合MySQL的入门学习教程

    零.关于Hibernate Hibernate是冬眠的意思,它是指动物的冬眠,但是本文讨论的Hibernate却与冬眠毫无关系,而是接下来要讨论的SSH2框架中的一员.Hibernate是一个开源的项目,它是一个对象关系模型的框架,并且对JDBC进行了非常轻量级的封装,程序员在开发时可以使用对象编程思维进行开发. 下载地址:http://hibernate.org/orm/downloads/ Note:轻量级和重量级的区别,轻量级的框架包较小,并且使用较简单,而且测试容易,开发效率高:重量级框

  • 快速了解hibernate配置文件与映射文件

    Hibernate是一个彻底的ORM(Object Relational Mapping,对象关系映射)开源框架. 我们先看一下官方文档所给出的,Hibernate 体系结构的高层视图: 其中PO=POJO+映射文件 根据体系结构视图可以了解到整个利用Hibernate框架实现的项目包括整个重要的配置文件: Hibernate配置文件:实现Hibernate基础配置,是Hibernate能够友好的与DB进行交互基础:开发时放置src目录下,取名为:hibernate.cfg.xml(hibern

  • java hibernate使用注解来定义联合主键

    java  hibernate使用注解来定义联合主键 下面使用hibernate的API中说明的三种方式来定义主键,主要使用Annotation来定义hibernate中的联合主键 下面取至hibernate的API文档: 定义组合主键的几种语法: 1.将组件类注解为@Embeddable,并将组件的属性注解为@Id 2.将组件的属性注解为@EmbeddedId 3.将类注解为@IdClass,并将该实体中所有属于主键的属性都注解为@Id 下面就分别使用这三种方式来定义联合主键. 建表的SQL语

  • Java Hibernate中使用HQL语句进行数据库查询的要点解析

    一.实体对象查询 实体对象查询是hql查询的基础,作为一种对象查询语言,在查询操作时和sql不同,查询字符串中的内容要使用类名和类的属性名来代替.这种查询方法相对简单,只要有SQL功底,使用hql是很简单的,但是有一些问题需要注意,就是查询获取数据不是目的,需要考虑的是如何编写出高效的查询语句,这才是讨论的重点. 1.N+1问题 (1)什么是N+1问题 在刚听到这个名词时疑惑可能是有的,以前根本就没有听过N+1问题,那么它是指什么呢?N+1指的是一张表中有N条数据,那么在获取这N条数据时会产生N

  • java中switch case语句需要加入break的原因解析

    java中switch case语句需要加入break的原因解析            java 中使用switch case语句需要加入break 做了具体的实例分析,及编译源码,在源码中分析应该如何使用,大家可以参考下: 假设我们有如下这样一个switch语句: public static void test(int index) { switch (index) { case 1: System.out.println(1); case 2: System.out.println(2);

  • iOS开发中使用SQL语句操作数据库的基本用法指南

    SQL代码应用示例 一.使用代码的方式批量添加(导入)数据到数据库中 1.执行SQL语句在数据库中添加一条信息 插入一条数据的sql语句: 点击run执行语句之后,刷新数据 2.在ios项目中使用代码批量添加多行数据示例 代码示例: 复制代码 代码如下: // //  main.m //  01-为数据库添加多行数据 // //  Created by apple on 14-7-26. //  Copyright (c) 2014年 wendingding. All rights reserv

  • 详解Java编程中if...else语句的嵌套写法

    if...else if...else语句 if语句后面可以跟elseif-else语句,这种语句可以检测到多种可能的情况. 使用if,else if,else语句的时候,需要注意下面几点: if语句至多有1个else语句,else语句在所有的elseif语句之后. If语句可以有若干个elseif语句,它们必须在else语句之前. 一旦其中一个else if语句检测为true,其他的else if以及else语句都将跳过执行. 语法 if...else语法格式如下: if(布尔表达式 1){

  • Android中ViewPager带来的滑动卡顿问题解决要点解析

    问题说明: 当SwipeRefreshLayout中放置了ViewPager控件,两者的滑动会相互冲突.具体表现为ViewPager的左右滑动不顺畅,容易被SwipeRefreshLayout拦截(即出现刷新的View). 问题原因: ViewPager本身是处理了滚动事件的冲突,它在横向滑动时会调用requestDisallowInterceptTouchEvent()方法使父控件不拦截当前的Touch事件序列.但是SwipeRefreshLayout的requestDisallowInter

  • iOS中的导航栏UINavigationBar与工具栏UIToolBar要点解析

    一.导航栏UINavigationBar 1.导航栏的使用 在iOS开发中,我们通常会使用导航控制器,导航控制器中封装了一个UINavigationBar,实际上,我们也可以在不使用导航控制器的前提下,单独使用导航栏,在UINavigationBar中,也有许多我们可以定制的属性,用起来十分方便. 2.UINavigationBar的创建和风格类型 导航栏继承于UIView,所以我们可以像创建普通视图那样创建导航栏,比如我们创建一个高度为80的导航栏,将其放在ViewController的头部,

  • 在Java程序中使用数据库的新方法

    Java 8终于到来了! 经过几年的等待, java程序员终于能在java中得到函数式编程的支持了. 函数式编程的支持能流程化现有的代码并且为java提供强大的能力.在这些新特性中最瞩目的是java程序员对数据库的操作方式.函数式编程带来了令人激动的简便高效的数据库API. Java 8 将会支持可与像C#的LINQ等语言竞争的新的数据库访问方式. 处理数据的函数式方式 Java 8 不仅仅添加了函数式支持,它也通过新的函数式处理数据的方式扩展了集合(Collection)类. 而通常情况下ja

  • java中switch选择语句代码详解

    switch结构(开关语句)的语法 switch(表达式 ){ --->类型为int.char case 常量1 :--->case 结构可以有多个 //语句块1 break; --->程序跳出switch结构 case 常量n :--->常量的值不能相同 //语句块n break; default:--->和if结构中的 else作用相同 //语句块 break; } 下面看一段代码示例,有详细的注释,大家可以参考: public class SwitchStu{ /* s

  • Hibernate中实现增删改查的步骤详解

    1.首先我们要知道什么是Hibernate Hibernate是一个轻量级的ORMapping对象.主要用来实现Java和数据库表之间的映射,除此之外还提供数据查询和数据获取的方法, 可以大幅度减少开发时人工使用SQL和JDBC处理数据的时间,解放编程人员95%的任务. 2.什么是ORM  Object-Relational-Mapping对象关系映射 ORM:是通过java对象映射到数据库表,通过操作Java对象可以完成对数据表的操作.(假如你用的是Dbutils那么还需要在Java类中写sq

  • iOS开发中如何优雅的调试数据库详解

    背景 写代码难免出现bug. 储备些调试技能绝对能够提高你的工作效率,让bug无所遁形.相信大家应该都有所体会,我们在开发的时候,数据库的操作一直是一个很棘手的问题,后来发现Android下面有一个第三方的库还挺好用的,就模仿它搞了个iOS的,可以方便的通过浏览器查看.添加.删除.修改数据库.下面话不多说了,来一看看详细的介绍吧. 历史状况 我们来回想一下调试的过程: 如果在模拟器中调试: 找到模拟器应用中数据库的文件位置 拷回到一个比较方便打开的地方 安装一个数据库操作软件 打开数据库文件 s

随机推荐