標籤:style class blog code java ext
上篇文章簡單介紹了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的串連查詢及統計查詢。