热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

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

HQL是Hibernate框架中提供的关系型数据库操作脚本,当然我们也可以使用原生的SQL语句,这里我们来看一下在JavaHibernate中使用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来获取查询内容。

四、外置命名查询

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

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

<&#63;xml version="1.0"&#63;> 
 
 
   
     
       
     
     
     
     
     
   
   
   
     
   
   
 

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

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


推荐阅读
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 本文讨论了在Spring 3.1中,数据源未能自动连接到@Configuration类的错误原因,并提供了解决方法。作者发现了错误的原因,并在代码中手动定义了PersistenceAnnotationBeanPostProcessor。作者删除了该定义后,问题得到解决。此外,作者还指出了默认的PersistenceAnnotationBeanPostProcessor的注册方式,并提供了自定义该bean定义的方法。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • Android系统移植与调试之如何修改Android设备状态条上音量加减键在横竖屏切换的时候的显示于隐藏
    本文介绍了如何修改Android设备状态条上音量加减键在横竖屏切换时的显示与隐藏。通过修改系统文件system_bar.xml实现了该功能,并分享了解决思路和经验。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 本文介绍了在使用MSXML解析XML文件时出现DTD禁用问题的解决方案。通过代码示例和错误信息获取方法,解释了默认情况下DTD是禁用的,以及如何启用DTD的方法。此外,还提到了网上关于该问题的信息相对较少,因此本文提供了解决方案以供参考。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • r2dbc配置多数据源
    R2dbc配置多数据源问题根据官网配置r2dbc连接mysql多数据源所遇到的问题pom配置可以参考官网,不过我这样配置会报错我并没有这样配置将以下内容添加到pom.xml文件d ... [详细]
author-avatar
手机用户2502861877
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有