首页 > 代码库 > 【Hibernate步步为营】--hql查询之实体对象查询
【Hibernate步步为营】--hql查询之实体对象查询
上篇文章简单介绍了hql它作为Hibernate的查询语言,封装了基本上SQL的所有查询操作,HQL能够实现对数据库文件的增删改查,该篇文章着重讨论HQL实体对象的查询方法。
一、实体对象查询
实体对象查询是hql查询的基础,作为一种对象查询语言,在查询操作时和sql不同,查询字符串中的内容要使用类名和类的属性名来代替。这种查询方法相对简单,只要有SQL功底,使用hql是很简单的,但是有一些问题需要注意,就是查询获取数据不是目的,需要考虑的是如何编写出高效的查询语句,这才是讨论的重点。
1.1 N+1问题
1.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语句。这种写法其实是一种错误,可以使用其它方法优化解决。
1.1.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问题。
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中配置相应的方言选项即可完成映射。
结语
对象查询方法是HQL不同于SQL的地方,在使用时加以区分即可。该篇文章主要针对对象查询的问题及查询方法做了详细的讨论另外还添加了SQL原生态的查询方法的使用。下篇文章将会详细讨论HQL的连接查询及统计查询。