天天看點

Hibernate的使用

  1. .主鍵的生成政策由使用者自己指定:assigned

② 。 随機取一定數量的資料

public List<Topic> getTopicsByExamInfo(String majorId,String exam_number){

        Session session=HibernateSessionFactory.getSession();

        String hql="from Topic t where t.topicMajorId=? order by rand()";

        Query query=session.createQuery(hql).setString(0, majorId);

        //需要拿到的題目數量

        int number=Integer.parseInt(exam_number);

        query.setMaxResults(number);

        List<Topic> list=query.list();

        HibernateSessionFactory.closeSession();

        return list;

    }

1、實體查詢:

有關實體查詢技術,其實我們在先前已經有多次涉及,比如下面的例子:

String hql="from User user ";

List list=session.CreateQuery(hql).list();

上面的代碼執行結果是,查詢出User實體對象所對應的所有資料,而且将資料封裝成User實體對象,并且放入List中傳回。這裡需要注意的 是,Hibernate的實體查詢存在着對繼承關系的判定,比如我們前面讨論映射實體繼承關系中的Employee實體對象,它有兩個子類分别是 HourlyEmployee,SalariedEmployee,如果有這樣的HQL語句:"from Employee",當執行檢索時Hibernate會檢索出所有Employee類型實體對象所對應的資料(包括它的子類 HourlyEmployee,SalariedEmployee對應的資料)。

因為HQL語句與标準SQL語句相似,是以我們也可以在HQL語句中使用where字句,并且可以在where字句中使用各種表達式,比較操作符以及使用"and","or"連接配接不同的查詢條件的組合。看下面的一些簡單的例子:

from User user where user.age=20;

from User user where user.age between 20 and 30;

from User user where user.age in(20,30);

from User user where user.name is null;

from User user where user.name like '%zx%';

from User user where (user.age%2)=1;

from User user where user.age=20 and user.name like '%zx%';

2、 實體的更新和删除:

在繼續講解HQL其他更為強大的查詢功能前,我們先來講解以下利用HQL進行實體更新和删除的技術。這項技術功能是Hibernate3的新加入的功能, 在Hibernate2中是不具備的。比如在Hibernate2中,如果我們想将資料庫中所有18歲的使用者的年齡全部改為20歲,那麼我們要首先将年齡 在18歲的使用者檢索出來,然後将他們的年齡修改為20歲,最後調用Session.update()語句進行更新。在Hibernate3中對這個問題提 供了更加靈活和更具效率的解決辦法,如下面的代碼:

Transaction trans=session.beginTransaction();

String hql="update User user set user.age=20 where user.age=18";

Query queryupdate=session.createQuery(hql);

int ret=queryupdate.executeUpdate();

trans.commit();

通過這種方式我們可以在Hibernate3中,一次性完成批量資料的更新,對性能的提高是相當的可觀。同樣也可以通過類似的方式來完成delete操作,如下面的代碼:

String hql="delete from User user where user.age=18";

如果你是逐個章節閱讀的化,那麼你一定會記起我在第二部分中有關批量資料操作的相關論述中,讨論過這種操作方式,這種操作方式在 Hibernate3中稱為bulk delete/update,這種方式能夠在很大程度上提高操作的靈活性和運作效率,但是采用這種方式極有可能引起緩存同步上的問題(請參考相關論述)。

3、 屬性查詢:

很多時候我們在檢索資料時,并不需要獲得實體對象所對應的全部資料,而隻需要檢索實體對象的部分屬性所對應的資料。這時候就可以利用HQL屬性查詢技術,如下面程式示例:

List list=session.createQuery("select user.name from User user ").list();

for(int i=0;i

System.out.println(list.get(i));

}

我們隻檢索了User實體的name屬性對應的資料,此時傳回的包含結果集的list中每個條目都是String類型的name屬性對應的資料。我們也可以一次檢索多個屬性,如下面程式:

List list=session.createQuery("select user.name,user.age from User user ").list();

Object[] obj=(Object[])list.get(i);

System.out.println(obj[0]);

System.out.println(obj[1]);

此時傳回的結果集list中,所包含的每個條目都是一個Object[]類型,其中包含對應的屬性資料值。作為當今我們這一代深受面向對象 思想影響的開發人員,可能會覺得上面傳回Object[]不夠符合面向對象風格,這時我們可以利用HQL提供的動态構造執行個體的功能對這些平面資料進行封 裝,如下面的程式代碼:

List list=session.createQuery("select new User(user.name,user.age) from User user ").list();

User user=(User)list.get(i);

System.out.println(user.getName());

System.out.println(user.getAge());

這裡我們通過動态構造執行個體對象,對傳回結果進行了封裝,使我們的程式更加符合面向對象風格,但是這裡有一個問題必須注意,那就是這時所傳回 的User對象,僅僅隻是一個普通的Java對象而以,除了查詢結果值之外,其它的屬性值都為null(包括主鍵值id),也就是說不能通過 Session對象對此對象執行持久化的更新操作。如下面的代碼:

user.setName("gam");

session.saveOrUpdate(user);//這裡将會實際執行一個save操作,而不會執行update操作,因為這個User對象的 id屬性為null,Hibernate會把它作為一個自由對象(請參考持久化對象狀态部分的論述),是以會對它執行save操作。

4、 分組與排序

A、Order by子句:

與SQL語句相似,HQL查詢也可以通過order by子句對查詢結果集進行排序,并且可以通過asc或者desc關鍵字指定排序方式,如下面的代碼:

from User user order by user.name asc,user.age desc;

上面HQL查詢語句,會以name屬性進行升序排序,以age屬性進行降序排序,而且與SQL語句一樣,預設的排序方式為asc,即升序排序。

B、Group by子句與統計查詢:

在HQL語句中同樣支援使用group by子句分組查詢,還支援group by子句結合聚集函數的分組統計查詢,大部分标準的SQL聚集函數都可以在HQL語句中使用,比如:count(),sum(),max(),min(),avg()等。如下面的程式代碼:

String hql="select count(user),user.age from User user group by user.age having count(user)>10 ";

List list=session.createQuery(hql).list();

C、優化統計查詢:

假設我們現在有兩張資料庫表,分别是customer表和order表,它們的結構如下:

customer

ID varchar2(14)

age number(10)

name varchar2(20)

order

order_number number(10)

customer_ID varchar2(14)

現在有兩條HQL查詢語句,分别如下:

from Customer c inner join c.orders o group by c.age;(1)

select c.ID,c.name,c.age,o.ID,o.order_number,o.customer_ID

from Customer c inner join c.orders c group by c.age;(2)

這兩條語句使用了HQL語句的内連接配接查詢(我們将在HQL語句的連接配接查詢部分專 門讨論),現在我們可以看出這兩條查詢語句最後所傳回的結果是一樣的,但是它們其實是有明顯差別的,語句(1)檢索的結果會傳回Customer與 Order持久化對象,而且它們會被置于Hibernate的Session緩存之中,并且Session會負責它們在緩存中的唯一性以及與背景資料庫數 據的同步,隻有事務送出後它們才會從緩存中被清除;而語句(2)傳回的是關系資料而并非是持久化對象,是以它們不會占用Hibernate的 Session緩存,隻要在檢索之後應用程式不在通路它們,它們所占用的記憶體就有可能被JVM 的垃圾回收器回收,而且Hibernate不會同步對它們的修改。

在我們的系統開發中,尤其是Mis系統,不可避免的要進行統計查詢的開發,這類功能有兩個特點:第一資料量大;第二一般情況下都是隻讀操作而不會涉及到對 統計資料進行修改,那麼如果采用第一種查詢方式,必然會導緻大量持久化對象位于Hibernate的Session緩存中,而且Hibernate的 Session緩存還要負責它們與資料庫資料的同步。而如果采用第二種查詢方式,顯然就會提高查詢性能,因為不需要Hibernate的Session緩 存的管理開銷,而且隻要應用程式不在使用這些資料,它們所占用的記憶體空間就會被回收釋放。

是以在開發統計查詢系統時,盡量使用通過select語句寫出需要查詢的屬性的方式來傳回關系資料,而避免使用第一種查詢方式傳回持久化對象(這種方式是 在有修改需求時使用比較适合),這樣可以提高運作效率并且減少記憶體消耗。㊣真正的高手并不是精通一切,而是精通在合适的場合使用合适的手段。

5、 參數綁定:

Hibernate中對動态查詢參數綁定提供了豐富的支援,那麼什麼是查詢參數動态綁定呢?其實如果我們熟悉傳統JDBC程式設計的話,我們就不難了解查詢參數動态綁定,如下代碼傳統JDBC的參數綁定:

PrepareStatement pre=connection.prepare("select * from User where user.name=?");

pre.setString(1,"zhaoxin");

ResultSet rs=pre.executeQuery();

在Hibernate中也提供了類似這種的查詢參數綁定功能,而且在Hibernate中對這個功能還提供了比傳統JDBC操作豐富的多的特性,在Hibernate中共存在4種參數綁定的方式,下面我們将分别介紹:

A、 按參數名稱綁定:

在HQL語句中定義命名參數要用":"開頭,形式如下:

Query query=session.createQuery("from User user where user.name=:customername and user:customerage=:age ");

query.setString("customername",name);

query.setInteger("customerage",age);

上面代碼中用:customername和:customerage分别定義了命名參數customername和 customerage,然後用 Query接口的setXXX()方法設定名參數值,setXXX()方法包含兩個參數,分别是命名參數名稱和命名參數實際值。

B、 按參數位置邦定:

在HQL查詢語句中用"?"來定義參數位置,形式如下:

Query query=session.createQuery("from User user where user.name=? and user.age =? ");

query.setString(0,name);

query.setInteger(1,age);

同樣使用setXXX()方法設定綁定參數,隻不過這時setXXX()方法的第一個參數代表邦定參數在HQL語句中出現的位置編号(由0開始編号),第二個參數仍然代表參數實際值。

注:在實際開發中,提倡使用按名稱邦定命名參數,因為這不但可以提供非常好的程式可讀性,而且也提高了程式的易維護性,因為當查詢參數的位置發生改變時,按名稱邦定名參數的方式中是不需要調整程式代碼的。

C、 setParameter()方法:

在Hibernate的HQL查詢中可以通過setParameter()方法邦定任意類型的參數,如下代碼:

String hql="from User user where user.name=:customername ";

Query query=session.createQuery(hql);

query.setParameter("customername",name,Hibernate.STRING);

如上面代碼所示,setParameter()方法包含三個參數,分别是命名參數名稱,命名參數實際值,以及命名參數映射類型。對于某些參 數類型 setParameter()方法可以更具參數值的Java類型,猜測出對應的映射類型,是以這時不需要顯示寫出映射類型,像上面的例子,可以直接這樣 寫:

query.setParameter("customername",name);但是對于一些類型就必須寫明映射類型,比如 java.util.Date類型,因為它會對應Hibernate的多種映射類型,比如Hibernate.DATA或者 Hibernate.TIMESTAMP。

D、 setProperties()方法:

在Hibernate中可以使用setProperties()方法,将命名參數與一個對象的屬性值綁定在一起,如下程式代碼:

Customer customer=new Customer();

customer.setName("pansl");

customer.setAge(80);

Query query=session.createQuery("from Customer c where c.name=:name and c.age=:age ");

query.setProperties(customer);

setProperties()方法會自動将customer對象執行個體的屬性值比對到命名參數上,但是要求命名參數名稱必須要與實體對象相應的屬性同名。

這裡還有一個特殊的setEntity()方法,它會把命名參數與一個持久化對象相關聯,如下面代碼所示:

Customer customer=(Customer)session.load(Customer.class,"1");

Query query=session.createQuery("from Order order where order.customer=:customer ");

query. setProperties("customer",customer);

List list=query.list();

上面的代碼會生成類似如下的SQL語句:

Select * from order where customer_ID='1';

E、 使用綁定參數的優勢:

我們為什麼要使用綁定命名參數?任何一個事物的存在都是有其價值的,具體到綁定參數對于HQL查詢來說,主要有以下兩個主要優勢:

①、可以利用資料庫實施性能優化,因為對Hibernate來說在底層使用的是PrepareStatement來完成查詢,是以對于文法相同參數不同的SQL語句,可以充分利用預編譯SQL語句緩存,進而提升查詢效率。

②、 可以防止SQL Injection安全漏洞的産生:

SQL Injection是一種專門針對SQL語句拼裝的攻擊方式,比如對于我們常見的使用者登入,在登入界面上,使用者輸入使用者名和密碼,這時登入驗證程式可能會生成如下的HQL語句:

"from User user where user.name='"+name+"' and user.password='"+password+"' "

這個HQL語句從邏輯上來說是沒有任何問題的,這個登入驗證功能在一般情況下也是會正确完成的,但是如果在登入時在使用者名中輸入"zhaoxin or 'x'='x",這時如果使用簡單的HQL語句的字元串拼裝,就會生成如下的HQL語句:

"from User user where user.name='zhaoxin' or 'x'='x' and user.password='admin' ";

顯然這條HQL語句的where字句将會永遠為真,而使使用者密碼的作用失去意義,這就是SQL Injection攻擊的基本原理。

而使用綁定參數方式,就可以妥善處理這問題,當使用綁定參數時,會得到下面的HQL語句:

from User user where user.name=''zhaoxin'' or ''x=''x'' ' and user.password='admin';由此可見使用綁定參數會将使用者名中輸入的單引号解析成字元串(如果想在字元串中包含單引号,應使用重複單引 号形式),是以參數綁定能夠有效防止SQL Injection安全漏洞。

窗體底端