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 |
案例一,多對一關聯,無迫切連線
案例二 多對一關聯,有迫切連線(注意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(); } } }
案例三 一對多關聯,有迫切連線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();
}
}
}