在Hibernate整個查詢體系中,存在以下幾種查詢語言:NativeSQL、HQL、EJBQL(JPQL)、QBC、QBE。他們之間的關係式NativeSQL>HQL>EJBQL(JPQL)>QBC>QBE。可以簡單認爲後者爲前者的子集,NativeSQL功能最爲強大。下面對於在Hibernate中用的查詢需要注意的幾點做一個總結:
第一:NativeSQL 與本地數據庫有關,由於是針對具體數據庫,所以不可以跨越數據庫平臺,但是也是功能最爲強大和最爲靈活的。一般在做本地報表系統的時候,會大量的使用NativeSQL。
第二:HQL,Hibernate Query Language ,這種語句在執行的時候,在系統內部會根據設置的Hibernate方言將其轉化爲具體的本地SQL。
第三:EJBQL(JPQL),95%以上的時候,都是使用的這個查詢語言,而且可以跨越ORMapping平臺。而且EJBQL是導航性質的。
第四:QL應該與導航關係相結合,共同爲查詢提供服務。
第五:關於分頁的實現:Oracle中使用三層嵌套實現,SQL使用Limit,而在HQL中使用的setMaxResult()和setFirstResult()兩個方法來實現。
第六:一個面試題:JAVA中有沒有內存泄露?其實,java在語法級別是沒有內存泄露的,但是在實際運用中,會存在內存泄露的情況,比如這樣一個場景,在一個有上千萬條數據的表中,不斷分頁取出數據,試想一下,每次取出一百條,如果處理完成之後,不手動的執行session.clear()清理內存,這個時候由於內存中有數據的引用,這個時候,垃圾收集器是不會主動去回收內存,這樣,在不斷的分頁取出數據的時候,一定時間以後,內存就會被佔滿。還有一個問題需要說明是:爲什麼java打開的文件一定要手動關閉?原因是JAVA打開一個文件操作實際是調用了底層的C來完成的,然後C再去調用windows API執行相關的操作,但是雖然JAVA有內存垃圾回收機制,但是C卻沒有這種機制。所以,JAVA打開的文件一定要手動的去關閉。
下面就常用的QL用法做一個總結說明:
1:HQL採用面向對象的思維,直接通過類取對象:比如 Query query = session.createQuery("from Category");
2:從面向對象的角度,在1的基礎上添加各種過濾條件:比如 Query q = session.createQuery("from Category c where c.name > 'c5'");
3:從面向對象的角度,在1的基礎上添加排序:Query q = session.createQuery("from Category c order by c.name desc");
4:過濾重複值:Query q = session.createQuery("select distinct c from Category c order by c.name desc");
5:設定過濾條件參數(兩種方式可選 ? 和 :xxx):
5.1:Query q = session.createQuery("from Category c where c.id > :min and c.id < :max order by c.name desc").setParameter("min", 3).setParameter("max", 6);
5.2:Query q = session.createQuery("from Category c where c.id > ? and c.id < ? order by c.name desc").setParameter(0, 3).setParameter(1, 6);
6:只選取對象中的特定列組成對象數組:
Query q = session.createQuery("select c.id , c.name from Category c");
List<Object[]> categories = (List<Object[]>)q.list();
7:通過對象導航實現查詢:Query q = session.createQuery("from Topic t where t.category.id = 1");8:將查詢出來的值組裝成VO對象:比如:Query q = session.createQuery("select new com.zxb.model.MsgInfo(m.id, m.cont, m.topic.title, m.topic.category.name) from Msg m");注意,這裏的MsgInfo對象在數據庫中是不存在對應表的,只是程序中用來完成特定功能的一個輔助工具類,比如VO ,DTO一般都屬於這類對象。
9:表之間的鏈接查詢:比如:Query q = session.createQuery("select t.title, c.name from Topic t join t.category c");注意,這裏的t.category c不可以是Category c,因爲在Topic 可能存在多個類型爲Category的屬性,這樣寫的話就不知道到底是以那個屬性作爲鏈接條件使用了。必須通過t.屬性名的形式指定具體是那個屬性作爲鏈接條件。
10:UniqueResult的使用:使用的過濾條件可以是傳入的一個對象。示例代碼如下:
Query q = session.createQuery("from Msg m where m = :MsgToSearch "); //不重要
Msg m = new Msg();
m.setId(1);
q.setParameter("MsgToSearch", m);
Msg mResult = (Msg)q.uniqueResult();
11:在HQL中,雖然從面向對象的角度考慮,但是sql中很多特性依舊可用,比如,聚合函數,between , in 等,例如:
Query q = session.createQuery("select count(*) from Msg m");
Query q = session.createQuery("select max(m.id), min(m.id), avg(m.id), sum(m.id) from Msg m");
Query q = session.createQuery("from Msg m where m.id between 3 and 5");
Query q = session.createQuery("from Msg m where m.id in (3,4, 5)");
Query q = session.createQuery("from Topic t where t.title like '_5'");
Query q = session.createQuery("select lower(t.title)," + "upper(t.title)," +"trim(t.title)," +"concat(t.title, '***')," + "length(t.title)" + "
from Topic t ");
Query q = session.createQuery("select t.title ,count(*) from Topic t group by t.title");
Query q = session.createQuery("from Topic t where exists (select m.id from Msg m where m.topic.id=t.id)") ;
12:null 、not null、 empty 、 not empty的說明,在HQL中,null 和 not null 是判斷對象的非集合屬性是否爲空,而empty 和 not empty使用來判斷對象的集合屬性是否有值,即集合的長度是否是0,而不是說空與非空,概念完全不一樣。
Query q = session.createQuery("from Msg m where m.cont is not null"); 判斷m的cont屬性是否是空值。
Query q = session.createQuery("from Topic t where t.msgs is empty");判斷t這個對象的集合屬性msgs的大小是否是0。
13:特別說明一個HQL語句:
Query q = session.createQuery("select current_date,current_time,current_timestamp ,t.id from Topic t");
這個hql語句中,取得的是數據庫的當前日期,時間以及時間戳信息,一般我們會認爲,服務器在數據庫服務器中取得這些當前信息有意義嗎?服務器去取得自己的這些信息不就ok了嗎?但是如果說在集羣環境下,這個時候,多個服務器需要通過時間信息相互協作,這個時候,有可能各個服務器的時間信息並不是一致的,這個時候就會出問題,但是如果都以數據庫服務器中的時間信息爲準的話,就不會存在問題。
14:命名sql: 這種方式,類似於配置文件的配置方式,先配好語句,用到的時候填充條件。比如。我們在要查詢的類上加上如下配置:(目前爲止:JAP還不支持@NameNativeQuery.)
@Entity
@NamedQueries({
@NamedQuery(name="topicQuery",query="from Topic t where t.id = :id")
})
/*@NamedNativeQueries({
@NamedNativeQuery(name="topicNativeQuery", query="select * from topic limit 2, 5")
})*/
public class Topic {
private int id;
private String title;
@Id
@GeneratedValue
public int getId() {
return id;
}
public String getTitle() {
return title;
}
public void setId(int id) {
this.id = id;
}
public void setTitle(String title) {
this.title = title;
}
}
由於我們配置了一條根據ID來查詢Topic類實例的命名語句,所以程序中每次我們要根據id取得topic實例的時候,我們就不需要每次都寫HQL語句了。只需要取得這個命名QL語句,然後填充條件即可。如下代碼所示:@Test
public void testNameQuery() {
Session session = this.sessionFactory.openSession();
session.beginTransaction();
Query q = session.getNamedQuery("topicQuery");
q.setParameter("id", 5);
Topic t = (Topic)q.uniqueResult();
System.out.println(t.getTitle());
session.getTransaction().commit();
session.close();
}
15:QBE查詢方式示例代碼:public void testQBE(){
Session session = this.sessionFactory.openSession();
session.beginTransaction();
Topic topic = new Topic();
topic.setTitle("T_");
Example example = Example.create(topic).enableLike().ignoreCase();
Criteria criteria = session.createCriteria(Topic.class)
.add(Restrictions.ge("id", 2))
.add(Restrictions.le("id", 9))
.add(example);
List<Topic> topics = (List<Topic>)criteria.list();
for(Topic t :topics ){
System.out.println(t.getTitle());
}
session.getTransaction().commit();
session.close();
}
16:QBC查詢方式示例代碼:public void testQBC(){
Session session = this.sessionFactory.openSession();
session.beginTransaction();
Criteria criteria = session.createCriteria(Topic.class)
.add(Restrictions.gt("id", 2))
.add(Restrictions.lt("id", 8))
.add(Restrictions.like("title", "t_"))
.createCriteria("category")
.add(Restrictions.between("id",1,5));
List<Topic> topics = (List<Topic>)criteria.list();
for(Topic t :topics ){
System.out.println(t.getTitle());
}
session.getTransaction().commit();
session.close();
}