1. 程式人生 > >hibernate系列十五:hql連線查詢,查詢效能優化,hql批量增刪改

hibernate系列十五:hql連線查詢,查詢效能優化,hql批量增刪改

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