• 【Hibernate 6】常用的hql语句以及N+1问题


    HQL:Hibernate Query Language,是Hibernate框架中的查询语言,十分接近于SQL语言!以下介绍一些常用的Hql语句:

    一、测试类

    Classes类:

    <span style="font-family:KaiTi_GB2312;font-size:18px;">package com.bjpowernode.hibernate;
    
    import java.util.Set;
    
    public class Classes {
    	
    	private int id;
    	
    	private String name;
    	
    	private Set students;
    
    	......
    	
    }</span>

    Student类:

    <span style="font-family:KaiTi_GB2312;font-size:18px;">package com.bjpowernode.hibernate;
    
    import java.util.Date;
    
    public class Student {
    
    	private int id;
    	
    	private String name;
    
    	private Date createTime;
    	
    	private Classes classes;
    	
    	public Student() {
    	}
    
    	public Student(int id, String name) {
    		this.id = id;
    		this.name = name;
    	}
    	
    	......
    }</span>

    初始化数据类:

    <span style="font-family:KaiTi_GB2312;font-size:18px;">package com.bjpowernode.hibernate;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    import org.hibernate.Session;
    
    public class InitData {
    
    	public static void main(String[] args) {
    			Session session = HibernateUtils.getSession();
    
    			try {
    				session.beginTransaction();
    
    				for(int i=0; i<10; i++){
    				
    					Classes classes = new Classes();
    					classes.setName("班级"+i);
    					session.save(classes);
    					
    					for(int j=0; j<10; j++){
    						Student student = new Student();
    						student.setName("班级"+i+"的学生"+j);
    						student.setCreateTime(randomDate("2009-07-01","2009-09-01"));
    						
    						//在内存中建立由student指向classes的引用
    						student.setClasses(classes);
    						session.save(student);
    					}
    				}
    				
    				for(int i=0; i<5; i++){
    					Classes classes = new Classes();
    					classes.setName("无学生班级"+i);
    					session.save(classes);
    				}
    				
    				for(int i=0; i<10; i++){
    					Student student = new Student();
    					student.setName("无业游民"+i);
    					session.save(student);
    				}
    				
    				session.getTransaction().commit();
    			} catch (Exception e) {
    				e.printStackTrace();
    				session.getTransaction().rollback();
    			} finally{
    				HibernateUtils.closeSession(session);
    			}
    		}	
    		
    		/**
    		 * 获取随机日期
    		 * @param beginDate 起始日期,格式为:yyyy-MM-dd
    		 * @param endDate 结束日期,格式为:yyyy-MM-dd
    		 * @return
    		 */
    		private static Date randomDate(String beginDate,String endDate){
    			try {
    				SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
    				Date start = format.parse(beginDate);
    				Date end = format.parse(endDate);
    				
    				if(start.getTime() >= end.getTime()){
    					return null;
    				}
    				
    				long date = random(start.getTime(),end.getTime());
    				
    				return new Date(date);
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    			return null;
    		}
    		
    		private static long random(long begin,long end){
    			long rtn = begin + (long)(Math.random() * (end - begin));
    			if(rtn == begin || rtn == end){
    				return random(begin,end);
    			}
    			return rtn;
    		}
    }</span>

    二、常用的hql语句

    2.1,简单属性查询

    单一属性:

    <span style="font-family:KaiTi_GB2312;font-size:18px;">//返回结果集属性列表,元素类型和实体类中的属性类型一致
    List students = session.createQuery("select name from Student").list();
    for (Iterator iter=students.iterator(); iter.hasNext();) {
    	String name = (String)iter.next();
    	System.out.println(name);
    }</span>
    多个属性:

    <span style="font-family:KaiTi_GB2312;font-size:18px;">//查询多个属性,返回对象数组集合,数组元素的类型与查询的属性类型一致
    //数组的长度与select中查询的属性个数一致
    List students = session.createQuery("select id, name from Student").list();
    for (Iterator iter=students.iterator(); iter.hasNext();) {
    	Object[] obj = (Object[])iter.next();
    	System.out.println(obj[0] + ", " + obj[1]);
    }</span>
    多个属性查询,返回实体对象

    <span style="font-family:KaiTi_GB2312;font-size:18px;">//可以使用hql返回Student对象
    <span style="color:#ff6666;">//需要提供构造函数</span>
    List students = session.createQuery("select new Student(id, name) from Student").list();
    for (Iterator iter=students.iterator(); iter.hasNext();) {
    	Student student = (Student)iter.next();
    	System.out.println(student.getId() + ", " + student.getName());
    }</span>

    2.2,带参数的查询

    拼串查询

    <span style="font-family:KaiTi_GB2312;font-size:18px;">List students = session.createQuery("select s.id, s.name from Student s where s.name like '%0%'").list();</span>
    使用?方式传递参数,索引从0开始,值能使用单引号引起来

    <span style="font-family:KaiTi_GB2312;font-size:18px;">//方法链编程,建议采用此种方式
    List students = session.createQuery("select s.id, s.name from Student s where s.name like ?")
    		.setParameter(0, "%0%")
    		.list();</span>

    <span style="font-family:KaiTi_GB2312;font-size:18px;">//采用 ?方式,查询学号为1,2,3,4,5的学生
    List students = session.createQuery("select s.id, s.name from Student s where s.id in(?, ?, ?, ?, ?)")
    		.setParameter(0, 1)
    		.setParameter(1, 2)
    		.setParameter(2, 3)
    		.setParameter(3, 4)
    		.setParameter(4, 5)
    		.list();</span>

    使用 :参数名的方式传递参数

    <span style="font-family:KaiTi_GB2312;font-size:18px;">//可以采用 :参数名 的方式传递参数
    List students = session.createQuery("select s.id, s.name from Student s where s.name like :myname")
    		.setParameter("myname",  "%0%")
    		.list();</span>
    <span style="font-family:KaiTi_GB2312;font-size:18px;">//采用 :参数名 方式,查询学号为1,2,3,4,5的学生
    List students = session.createQuery("select s.id, s.name from Student s where s.id in(:ids)")
    		.setParameterList("ids", new Object[]{1, 2, 3, 4, 5})
    		.list();</span>

    2.3,对象导航查询(通过cross join查询,慎重考虑使用)

    <span style="font-family:KaiTi_GB2312;font-size:18px;">List students = session.createQuery("from Student s where s.classes.name like '%2%'")
    		.list();</span>

    2.4,外置命名查询

    实体映射文件中的配置:

    <span style="font-family:KaiTi_GB2312;font-size:18px;"><query name="queryStudent">
    	<![CDATA[
    		select s from Student s where s.id <?
    	]]>
    </query>	</span>
    查询时的调用:

    <span style="font-family:KaiTi_GB2312;font-size:18px;">List students = session.getNamedQuery("queryStudent")
    		.setParameter(0, 10)
    		.list();</span>

    2.5,分页查询

    <span style="font-family:KaiTi_GB2312;font-size:18px;">List students = session.createQuery("from Student")
    		.setFirstResult(1)
    		.setMaxResults(2)
    		.list();</span>
    setFirstResult:表示第一条记录的索引;setMaxResults:表示每页显示的最大记录数

    2.6,原生sql语句查询

    <span style="font-family:KaiTi_GB2312;font-size:18px;">List students = session.createSQLQuery("select * from t_student").list();</span>

    2.7,统计查询(实际上是调用函数的查询)

    <span style="font-family:KaiTi_GB2312;font-size:18px;">String hql = "select c.name, count(s) from Classes c join c.students s group by c.name order by c.name";
    List students = session.createQuery(hql).list();</span>

    2.8,过滤查询

    实体对象映射文件配置:

    <pre name="code" class="html"><span style="font-family:KaiTi_GB2312;font-size:18px;">	<class name="com.bjpowernode.hibernate.Student" table="t_student">
    		<id name="id">
    			<generator class="native"/>
    		</id>
    		<property name="name"/>
    		<property name="createTime"/>
    		<many-to-one name="classes" column="classesid"/>
    		<span style="color:#ff6666;"><filter name="testFilter" condition="id < :myid"></filter></span>
    	</class>
    	
    	<span style="color:#ff6666;"><filter-def name="testFilter">
    		<filter-param type="integer" name="myid"/>
    	</filter-def></span></span>

    
    

    <span style="font-family:KaiTi_GB2312;font-size:18px;">session.enableFilter("testFilter")
    	.setParameter("myid", 10);
    List students = session.createQuery("from Student").list();</span>


    以上就是一些常用的hql语句,下面介绍一下Hibernate中的N+1问题

    三、N+1问题

    所谓的N+1问题实际上是指Hibernate在查询的时候,发出了N+1条sql语句:

    1:发出一条查询id列表的语句

    N:根据id发出N条sql语句,加载相关对象

    Iterator接收查询结果:

    <pre name="code" class="java"><span style="font-family:KaiTi_GB2312;font-size:18px;">Iterator iter = session.createQuery("from Student").iterate();
    while (iter.hasNext()) {
    	Student student = (Student)iter.next();
    	System.out.println(student.getName());
    }</span>

    
    
    控制台打印结果:



    解决方案:

    每次执行list操作后会将数据放到session的缓存中(一级缓存),所以采用iterate的时候, 首先会发出一条查询id列表的语句,再根据id到缓存中加载相应的数据,如果缓存中存在与之匹配的数据,则不再发出根据id查询的sql语句,直接使用缓存中的数据。

    所以,使用list去接收查询的语句,可避免N+1的问题。但如果缓存中存在数据,使用Iterate会比使用List性能高!

    注意: 在默认情况下,每次执行list查询实体对象都会发出查询语句,除非配置了查询缓存。虽然一级缓存中存在Student数据,但list不用,所以仍然发出查询语句, 其实list就是只向缓存中放入数据,而不利用缓存中的数据


    四、总结

    以上就是使用Hibernate框架在查询中需要注意的一些问题。在谈到N+1问题的时候,说到了缓存。那么缓存是提高Hibernate整个框架性能的一个重要因素,下篇博客谈谈Hibernate的缓存机制。它的一级缓存、二级缓存等的开启和管理!

  • 相关阅读:
    Android性能优化--ANR
    Android性能优化--冷启动优化(Application)
    Android性能优化--UI卡顿
    Android性能优化--内存泄漏
    java反射
    数据结构之约瑟夫问题(循环链表)(C++版)
    数据结构之杨辉三角(队列实现)(C++版)
    vue组件间通信六种方式
    类型转换
    千分位
  • 原文地址:https://www.cnblogs.com/hhx626/p/6010315.html
Copyright © 2020-2023  润新知