首页 > 代码库 > 【Hibernate步步为营】--hql查询过滤器及相关聚合函数查询详解

【Hibernate步步为营】--hql查询过滤器及相关聚合函数查询详解

        上篇文章讨论了hql查询中的连接查询,它的查询语法在功能上和sql的连接查询是相同的,内连接查询取得的是关系之间的笛卡尔积,外连接查询是获取一个关系表及与另一个关系表的合集部分,具体的使用方法见上篇文章,并在最后讨论了外置命名查询的方法。该篇文章将会对hql的参数查询、函数查询及查询过滤器做详细的讨论。


一、参数查询


        参数查询其实是使用等价代换的方法,使用设置的值替换字符串中指定位置的符号,或者通过使用参数名称,使用字符串来替换参数名称的值,这样能避免sql注入的问题,拼接字符串会出现sql注入的问题。hql提供了两种参数查询的方法,一种是通过使用符号“?”,然后使用setParameter方法设置替换的字符串;另外一种是使用参数名称,定义参数名称,并使用setParameter方法来替换指定参数名称的字串。


 1.1 参数符号--?


        通过使用符号?来设置要添加的参数内容,也就是说这里的符号?其实是hql的参数。如果想要为参数赋值,可以使用hql的方法setParameter,该方法重载参数,提供两个参数,一个能够传递参数出现的位置,另一个为参数赋值。具体如下代码:

@SuppressWarnings({ "unchecked", "rawtypes" })
public void testQuery(){
	Session session=null;
	
	try{
		session=HibernateUtils.getSession();
		session.beginTransaction();
		
		//方法链编程
		List students=session.createQuery("select s from Student s where s.name like ?").setParameter(0, "%0%").list();
		for(Iterator iter=students.iterator();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);
	}
}

       在需要使用参数的字串位置处使用?来代替,然后使用setParameter 方法,替换参数的内容。该方法中的第一个参数可以是字符串也可以是索引,如果使用了?作为参数,就必须指定?的第几个索引。


  1.2 参数名称


       上文已经讨论了符号参数的使用方法,并稍微简介了参数名称查询的方法,也就是说可通过使用参数名称的方法来定义参数,这种方法需要指定参数的名称,然后使用setParameter来设置名称替换参数,hql的参数定义是冒号+参数名。

@SuppressWarnings({ "unchecked", "rawtypes" })
public void testQuery(){
	Session session=null;
	
	try{
		session=HibernateUtils.getSession();
		session.beginTransaction();
		
		//可以采用 :参数名 的方式传递参数
		
		//方法链编程
		List students=session.createQuery("select s from Student s where s.name like :myname").setParameter("myname", "%0%").list();
		for(Iterator iter=students.iterator();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);
	}
}


        在上面的示例中使用了like模糊查询,查询在学生的姓名中有0出现的所有的学生信息,查询字符串中定义了一个名称为myname的参数,并使用setParameter为参数赋值。


  1.3 参数集合


        上文简单介绍了hql单个参数的使用方法,非常简单,那如果字串中需要配置多个连接的参数怎么办呢,那要使用setParameter一个个的进行赋值吗?hql提供了setParameterList来为参数舒服赋值,如下示例:

@SuppressWarnings({ "unchecked", "rawtypes" })
public void testQuery5(){
	Session session=null;
	try{
		session=HibernateUtils.getSession();
		session.beginTransaction();
		
		//可以采用 :参数名 的方式传递参数
		
		//方法链编程
		List students=session.createQuery("select s from Student s where s.id in(:ids)")
				.setParameterList("ids",new Object[]{1,2,3,4})
				.list();
		for(Iterator iter=students.iterator();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);
	}
}

      上例中使用了集合查询in,在集合中查询符合集合要求的内容,定义了名为ids的参数,参数的具体内容是一个对象集合,使用setParameterList为参数做了赋值。


二、函数查询


        hql的具体查询提供了基本的sql查询方法,同样也支持对应数据库的函数查询方法,可以在查询语句中使用数据库函数来设置查询内容的格式及想要查询的内容,如日期格式函数date_format:

@SuppressWarnings({ "unchecked", "rawtypes" })
public void testQuery(){
	Session session=null;
	try{
		session=HibernateUtils.getSession();
		session.beginTransaction();
		
		//日期函数
		List students=session.createQuery("select s from Student s where date_format(s.createTime,'%Y-%m')=?")
				.setParameter(0,"2009-07")
				.list();
		for(Iterator iter=students.iterator();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);
	}
}

      上例中是按照学生信息的创建时间来查询相应的学生信息,通过使用date_format()方法来将字符串转化为相应查询内容的格式来获取的查询内容。     

      另外也可以使用数据库的between...and...来查询某一范围内的数据,如下示例查询某一时间段内的学生信息:

@SuppressWarnings({ "unchecked", "rawtypes" })
public void testQuery(){
	Session session=null;
	try{
		session=HibernateUtils.getSession();
		session.beginTransaction();
		
		//查询时间段
		List students=session.createQuery("select s from Student s where date_format(s.createTime,'%Y-%m') between ? and ?")
				.setParameter(0,"2009-07")
				.setParameter(1,"2012-07")
				.list();
		for(Iterator iter=students.iterator();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);
	}
}

        聚合查询,hql不支持*查询,但是可以使用count(*)查询。想要使用group by分组语句就必须在语句使用使用聚合函数。如下示例演示的是hql聚合函数和分组查询的使用方法:

@SuppressWarnings({ "unchecked", "rawtypes" })
public void testQuery(){
	Session session=null;
	
	try{
		session=HibernateUtils.getSession();
		session.beginTransaction();
		
		//返回结果集属性列表,元素类型和实体类中的属性类型一致
		List students=session.createQuery("select c.name,count(s) from Classes c join c.students s group by c.name order by c.name").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);
	}
}


       获取第一行,hql提供了方法来获取结果集中的第一行数据,该方法为uniqueResult(),通过使用sql语句来获取的结果集,然后在方法链中使用uniqueResult()方法来获取默认第一行的数据,如下代码:

<pre name="code" class="java">@SuppressWarnings({ "unchecked", "rawtypes" })
public void testQuery(){
	Session session=null;
	
	try{
		session=HibernateUtils.getSession();
		session.beginTransaction();
		
		//返回结果集属性列表,元素类型和实体类中的属性类型一致
		//List students=session.createQuery("select count(*) from Student").list();
		//Long count=(Long)students.get(0);

		//上面的查询方法,类似于下面使用的uniqueResult()方法
		Long count=(Long)session.createQuery("select count(*) from Student").uniqueResult();
		System.out.println("count="+count);
		session.getTransaction().commit();
	}catch(Exception e){
		e.printStackTrace();
		session.getTransaction().rollback();
	}finally{
		HibernateUtils.closeSession(session);
	}
}

      上面的代码示例提供了两种方法来获取第一行的数据,一种是通过使用get()方法,获取结果集中的第几行的内容;另外一种使用的是uniqueResult()方法来获取新行的内容。


三、查询过滤器


       查询过滤器提供了对查询的内容进行过滤的过程,在映射文件中定义过滤器,程序中启用过滤器,并为过滤器参数赋值。具体步骤为:首先在要查询的实体对象的映射中使用<filter-def>标签配置过滤器,并在相对应的<class>标签中添加对应的<filter>过滤器;然后在对应的程序文件中使用enableFilter()方法启动过滤器,为过滤器定义的参数赋值,在执行时就会自动自行对应的过滤器。

       清单一:映射文件,在映射文件中配置对应的过滤器,映射文件中定义了名为testFilter的过滤器,在class中为过滤器添加了执行条件,并在条件中添加了执行参数myid。

<?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>
		<!-- 使用过滤器,指定id的内容要小于参数:myid配置的内容-->
		<filter name="testFilter" condition="id < :myid"></filter>
	</class>
	
	<!-- 定义查询过V领器 -->
	<filter-def name="testFilter">
		<filter-param type="integer" name="myid"></filter-param>
	</filter-def>
</hibernate-mapping>


       清单二:程序文件,在程序文件中使用enableFilter启用过滤器,并为过滤器的参数赋值。

@SuppressWarnings({ "unchecked", "rawtypes" })
public void testQuery(){
	Session session=null;
	
	try{
		session=HibernateUtils.getSession();
		session.beginTransaction();
		
		session.enableFilter("testFilter").setParameter("myid",10);
		//返回结果集属性列表,元素类型和实体类中的属性类型一致
		List students=session.createQuery("from Student").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);
	}
}

    

结语


        hql的基本查询方法已经讨论完成,主要针对hql的使用过程中需要注意的内容作了详细的讨论,在每一种查询方法中都添加了相应的示例,通过示例加深对hql查询方法的使用,有sql基础的在使用hql时会非常的简单。