一 hql连接查询

和SQL查询一样,HQL也支持各种各样的连接查询,如内连接、外连接。我们知道在SQL中可通过join子句实现多表之间的连接查询。HQL同样提供了连接查询机制,还允许显式指定迫切内连接和迫切左外连接。HQL提供的连接方式如表1所示。

连接类型

HQL语法

适用范围

内连接

inner join或 join

适用于有关联关系

迫切内连接

inner join fetch或 join fetch

的持久化类,并且在

左外连接

left outer join或 left join

映射文件中对这种关

迫切左外连接

left outer join fetch或 left join fetch

联关系做了映射

右外连接

right outer join或 right join

 

迫切连接是指在指定连接方式时不仅指定了连接查询方式,而且显式地指定了关联级别的查询策略。Hibernate使用fetch关键字实现,fetch关键字表明“左边”对象用于与“右边”对象关联的属性会立即被初始化。


案例一,多对一关联,无迫切连接

package com.obtk.test2;

import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;

import com.obtk.entitys.DeptEntity;
import com.obtk.entitys.EmployeeEntity;
import com.obtk.utils.HibernateUtil;

public class TestJoin01 {
	public static void main(String[] args) {
		Session session=null;
		String hql="from EmployeeEntity e inner join e.dept d " +
				" where e.gender=? and d.deptName=? ";
		try {
			session=HibernateUtil.getSession();
			Query qy=session.createQuery(hql);
			qy.setParameter(0, "男");
			qy.setParameter(1, "人事部");
			List<Object[]> objList=qy.list();
			EmployeeEntity theEmp=null;
			DeptEntity dept=null;
			for(Object[] theArr :objList){
				theEmp=(EmployeeEntity)theArr[0];
				dept=(DeptEntity)theArr[1];
				System.out.println(theEmp.getEmpName()+","+theEmp.getGender()
						+","+dept.getDeptName());
			}
		} catch (HibernateException e) {
			e.printStackTrace();
		}finally{
			HibernateUtil.closeSession();
		}
	}
}

案例二   多对一关联,有迫切连接(注意fetch关键字)


package com.obtk.test2;

import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;

import com.obtk.entitys.DeptEntity;
import com.obtk.entitys.EmployeeEntity;
import com.obtk.utils.HibernateUtil;

public class TestFetchJoin01 {
	public static void main(String[] args) {
		Session session=null;
		String hql="from EmployeeEntity e inner join fetch e.dept d " +
				" where e.gender=? and d.deptName=? ";
		try {
			session=HibernateUtil.getSession();
			Query qy=session.createQuery(hql);
			qy.setParameter(0, "男");
			qy.setParameter(1, "人事部");
			List<EmployeeEntity> empList=qy.list();
			for(EmployeeEntity theEmp:empList){
				System.out.println(theEmp.getEmpName()+","+theEmp.getGender()
						+","+theEmp.getDept().getDeptName());
			}
		} catch (HibernateException e) {
			e.printStackTrace();
		}finally{
			HibernateUtil.closeSession();
		}
	}
}

案例三  一对多关联,有迫切连接

package com.obtk.test2;

import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;

import com.obtk.entitys.DeptEntity;
import com.obtk.entitys.EmployeeEntity;
import com.obtk.utils.HibernateUtil;

public class TestFetchJoin02 {
	public static void main(String[] args) {
		Session session=null;
		String hql="select distinct d from DeptEntity d inner join fetch d.empList e  " +
				" where e.gender=? ";
		try {
			session=HibernateUtil.getSession();
			Query qy=session.createQuery(hql);
			qy.setParameter(0, "男");
			List<DeptEntity> deptList=qy.list();
			List<EmployeeEntity> empList=null;
			for(DeptEntity theDept:deptList){
				empList=theDept.getEmpList();
				for(EmployeeEntity theEmp: empList){
					System.out.println(theEmp.getEmpName()+","+theEmp.getGender()
							+","+theDept.getDeptName());
				}
			}
		} catch (HibernateException e) {
			e.printStackTrace();
		}finally{
			HibernateUtil.closeSession();
		}
	}
}

建议用多对一,也就是从外键表发出关联,这样性能会好些,输出的sql会少些。

案例四  隐式内连接

package com.obtk.test2;

import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;

import com.obtk.entitys.EmployeeEntity;
import com.obtk.utils.HibernateUtil;

public class TestYinJoin {
	public static void main(String[] args) {
		Session session=null;
		//隐式内连接
		String hql="from EmployeeEntity e where e.gender=? and e.dept.deptName=?";
		try {
			session=HibernateUtil.getSession();
			Query qy=session.createQuery(hql);
			qy.setParameter(0, "男");
			qy.setParameter(1, "人事部");
			List<EmployeeEntity> empList=qy.list();
			for(EmployeeEntity theEmp:empList){
				System.out.println(theEmp.getEmpName()+","+theEmp.getGender()
						+","+theEmp.getDept().getDeptName());
			}
		} catch (HibernateException e) {
			e.printStackTrace();
		}finally{
			HibernateUtil.closeSession();
		}
	}
}



案例五,迫切左外连接


package com.obtk.test2;

import java.util.List;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;

import com.obtk.entitys.DeptEntity;
import com.obtk.entitys.EmployeeEntity;
import com.obtk.utils.HibernateUtil;

public class TestLeftJoin01 {
	public static void main(String[] args) {
		Session session=null;
		String hql="from EmployeeEntity e left join fetch e.dept d ";
		try {
			session=HibernateUtil.getSession();
			Query qy=session.createQuery(hql);
			List<EmployeeEntity> empList=qy.list();
			for(EmployeeEntity theEmp:empList){
				if(theEmp.getDept()!=null){
					System.out.println(theEmp.getEmpName()+","+theEmp.getGender()
							+","+theEmp.getDept().getDeptName());
				}else{
					System.out.println(theEmp.getEmpName()+","+theEmp.getGender()
							+",null");
				}
				
			}
		} catch (HibernateException e) {
			e.printStackTrace();
		}finally{
			HibernateUtil.closeSession();
		}
	}
}

fetch关键字只对inner join和left join有效。对于right join而言,由于作为关联对象容器的“左边”对象可能为null,所以也就无法通过fetch关键字强制Hibernate进行集合填充操作。

二  查询性能优化

 1.Hibernate主要从以下几方面来优化查询性能

    (1)使用迫切左外连接或迫切内连接查询策略、查询缓存等方式,减少select语句的数目,降低访问数据库的频率。

    (2)使用延迟查询策略等方式避免加载多余的不需要访问的数据。

    (3)使用Query接口的iterate()方法减少select语句中的字段,从而降低访问数据库的数据量。

    Query接口的iterate()方法和list()方法都用来执行SQL查询语句。在某些情况下,iterate()方法能提高查询性能。如示例1和示例2所示,先查询所有学生信息,再查询符合条件的学生信息,这种情况下,对比list()方法和iterate()方法的查询性能。

示例l

   List<StudentEntity>stuList=session.createQuery("from StudentEntity").list();

   for(StudentEntity student: stuList){

        System.out.println(student.getStuName());

   }

   stuList=session.createQuery("fromStudentEntity where stuId<2020").list();

   for(StudentEntity student: stuList){

       System.out.println(student.getStuName());

   }

运行示例l,Hibernate执行的select语句:

   select * from StudentEntity

   select * from StudentEntity wherestuId<2020

 

    第一次使用list()方法查询时,Hibernate从数据库中查询StudentEntity对象,把所有的StudentEntity对象放入Session缓存中;第二次使用list()查询时,Hibernate仍然从数据库中查询StudentEntity对象,而不是从Session缓存中获取StudentEntity对象。使用iterate()方法,能提高查询性能,如示例2所示。

示例2

List<StudentEntity>stuList=session.createQuery("from StudentEntity").list();

for(StudentEntitystudent: stuList){

       System.out.println(student.getStuName());

}

Iterator<StudentEntity>studentIt= session.createQuery("from StudentEntity wherestuId<2020").iterate();

StudentEntitystudent=null;

while(studentIt.hasNext()){

   student= studentIt.next();

   System.out.println(student.getStuName());

}

 

运行示例2,Hibernate执行的select语句:

   select * from StudentEntity

   select * from StudentEntity wherestuId<2020

 

    第一次使用list()方法查询时,Hibernate从数据库中查询StudentEntity对象,把所有的StudentEntity对象放入Session缓存中;第二次使用iterate()方法查询时,首先查询ID字段,然后根据ID字段到Hibernate的Session缓存中查找匹配的StudentEntity对象。如果存在,就直接把它加入到查询结果集中,否则就执行额外的select语句,根据ID字段到数据库中查询对象。

    iterate()方法适用于查询对象开启二级缓存的情况。

 2.HQL优化

    HQL优化是Hibernate程序性能优化的一个方面,HQL的语法与SQL非常类似。HQL是基于SQL的,只是增加了面向对象的封装。如果抛开HOL同Hibernate本身一些缓存机制的关联,HQL的优化技巧同SQL的优化技巧一样。在编写HQL时,需注意以下几个原则。

    (1)避免or操作的使用不当。如果where子句中有多个条件,并且其中某个条件没有索引,使用or,将导致全表扫描。假定在HOUSE表中TITLE有索引,PRICE没有索引,执行以下HQL语句:

    from House   where title= ‘出租-居室’  or price<1500

    当PRICE比较时,会引起全表扫描。

    (2)避免使用not。如果where子句的条件包含not关键字,那么执行时该字段的索引失效。这些语句需要分成不同情况区别对待,如查询租金不多于1800元的店铺出租转让信息的HQL语句:

    from House as h where not (h.price>1800)

    对于这种不大于(不多于)、不小于(不少于)的条件,建议使用比较运算符来替代not,如不大于就是小于等于。例如:

    from House as h where h.price<=1800

    如果知道某一字段所允许的设置值,那么就有其他的解决方法。例如,在用户表中增加性别字段,规定性别字段仅能包含M和F,当要查询非男用户时,为避免使用not关键字,将条件设定为查询女用户即可。

    (3)避免like的特殊形式。某些情况下,会在where子句条件中使用like。如果like以一个“%” 或“_”开始即前模糊,则该字段的索引不起作用。但是非常遗憾的是,对于这种问题并没有额外的解决方法,只能通过改变索引字段的形式变相地解决。

    (4)避免having予句。在分组的查询语句中,可在两个位置指定条件,一是where子旬中,二是在having子句中。尽可能地在where子句而不是having子句中指定条件。Having是在检索出所有记录后才对结果集进行过滤,这个处理需要一定的开销,而where子句限制记录数目,能减少这方面的开销。

    (5)避免使用distinct。指定distinct会导致在结果中删除重复的行。这会对处理时间造成一定的影响,因此在不要求或允许冗余时,应避免使用distinct。

    (6)索引在以下情况下失效,应注意使用。

Ø 只要对字段使用函数,该字段的索引将不起作用,如substring(aa,1,2)='xx'。

Ø 只要对字段进行计算,该字段的索引将不起作用,如price+10。



三  hql批量增删改


批量处理数据是指在一个事务场景中处理大量数据。

HQL可以查询数据,也可以批量插入、更新和删除数据。HQL批量操作实际上直接在数据库中完成,处理的数据不需要加载至Session缓存中。使用Query接口的executeUpdate()方法执行用于插入、更新和删除的HQL语句。

案例一  批量添加

package com.obtk.test03;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.obtk.utils.HibernateUtil;

public class TestAdd {
	public static void main(String[] args) {
		Session session=null;
		Transaction tx=null;
		String hql="insert into UserEntity(userName,passWord,email) " +
				" select userName,passWord,email from UserEntity";
		try {
			session=HibernateUtil.getSession();
			tx=session.beginTransaction();
			Query qy=session.createQuery(hql);
			int result=qy.executeUpdate();
			System.out.println("fdsa:"+result);
			tx.commit();
		} catch (HibernateException e) {
			tx.rollback();
			e.printStackTrace();
		}finally{
			HibernateUtil.closeSession();
		}
	}
}

案例二   批量删除

package com.obtk.test03;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.obtk.utils.HibernateUtil;

public class TestDelete {
	public static void main(String[] args) {
		Session session=null;
		Transaction tx=null;
		String hql="delete from UserEntity where userId>?";
		try {
			session=HibernateUtil.getSession();
			tx=session.beginTransaction();
			Query qy=session.createQuery(hql);
			qy.setParameter(0, 7);
			int result=qy.executeUpdate();
			System.out.println("fdsa:"+result);
			tx.commit();
		} catch (HibernateException e) {
			tx.rollback();
			e.printStackTrace();
		}finally{
			HibernateUtil.closeSession();
		}
	}
}

案例三  批量修改

package com.obtk.test03;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.obtk.utils.HibernateUtil;

public class TestUpdate {
	public static void main(String[] args) {
		Session session=null;
		Transaction tx=null;
		String hql="update UserEntity set passWord=? ";
		try {
			session=HibernateUtil.getSession();
			tx=session.beginTransaction();
			Query qy=session.createQuery(hql);
			qy.setParameter(0, "321");
			int result=qy.executeUpdate();
			System.out.println("fdsa:"+result);
			tx.commit();
		} catch (HibernateException e) {
			tx.rollback();
			e.printStackTrace();
		}finally{
			HibernateUtil.closeSession();
		}
	}
}