JPA, hibernate, jdbcTemplate(建議使用)區別
Hibernate批量處理海量其實從效能上考慮,它是很不可取的,浪費了很大的記憶體。從它的機制上講,Hibernate它是先把符合條件的資料查出來,放到記憶體當中,然後再進行操作。實際使用下來效能非常不理想.
spring jdbctemplate和hibernate在處理簡單查詢操作時,效率基本相同,甚至hibernate的效率要略高一些。但是在執行批量操作,繁瑣操作時,hibernate的效率能達到spring jdbctemplate的80%就不錯了。但hibernate可以極大提高開發效率,像分頁等較複雜的開發都是可以直接完成的,所以彌補了效率的不足。
總結一下:追求執行效率則spring jdbc;追求開發效率則hibernate;
如果SQL語句存在較多批量操作,建議spring jdbc。
如果真的要用hibernate,那要合理使用,例如:
http://blog.csdn.net/abcd1101/article/details/64189755
JPA的總體思想和現有hibernate、TopLink,JDO等ORM框架大體一致.JPA是需要Provider來實現其功能的,Hibernate就是JPA Provider中很強的一個.
Jpa是一種規範,而Hibernate是它的一種實現。除了Hibernate,還有EclipseLink(曾經的toplink),OpenJPA等可供選擇,所以使用Jpa的一個好處是,可以更換實現而不必改動太多程式碼。
我想拋開jpa,直接使用hibernate的註解來定義Model,很快發現了幾個問題:
- jpa中有Entity, Table,hibernate中也有,但是內容不同
- jpa中有Column,OneToMany等,Hibernate中沒有,也沒有替代品
我原以為hibernate對jpa的支援,是另提供了一套專用於jpa的註解,但現在看起來似乎不是。一些重要的註解如Column, OneToMany等,hibernate沒有提供,這說明jpa的註解已經是hibernate的核心,hibernate只提供了一些補充,而不是兩套註解。要是這樣,hibernate對jpa的支援還真夠足量,我們要使用hibernate註解就必定要使用jpa。
2.為什麼選擇jdbcTemplate
原因1:
本人在開發的過程中接觸到的持久層包括 hibernate 、mybatis .. 兩種持久層框架都感覺不盡如人意。首先說說hibernate,hibernate對資料庫操作進行了重量級封裝,使得對資料庫的操作完全是面向物件的操作,其中的面向物件操作成為很多開發人員的福音。但是hql就不那麼好了,hql雖然是sql的簡化版本,但是hql畢竟是在sql上面套了一層,如果有一天你的程式在那一塊操作變的異常的緩慢,那麼對hql優化將會變得比較間艱難,而且hibernate底層大量使用了反射機制,雖然其反射大多數情況都是在程式初始化時執行的,但是在程式執行過程中反射機制也是存在的。而mybatis是持久層的輕量級封裝,在mybatis中如果需要進行某一個操作,首先需要定義mapper,然後再定義mapper.xml。在mapper.xml中需要完成model對映,需要寫上介面相關的sql,這個過程作者認為重複性的工作比較大。如果能夠在Java類中直接書寫sql,同時還能夠進行簡單的物件操作,那麼程式將既擁有hibernate的部分有點,又將擁有mybatis的部分有點。在今後的開發中,我將會使用spring jdbcTemplate進行開發。
jdbcTemplate 的缺點
jdbcTemplate開發時不需要定義那麼多的xml,不會有hql語句優化艱難的缺點,但是其存在一個致命性的問題,就是太過基礎,基礎的就像在使用jdbc進行操作。這嚴重影響了開發的效率。不過也正因為其基礎性非常好,才使得其優化具有可行性。
原因2:
Hibernate批量更新是指在一個事務中更新大批量資料,Hibernate批量刪除是指在一個事務中刪除大批量資料。以下程式直接通過Hibernate API批量更新CUSTOMERS表中年齡大於零的所有記錄的AGE欄位:
1. tx = session.beginTransaction();
2. Iterator customers=session.find("from Customer c where c.age>0").iterator();
3. while(customers.hasNext()){
4. Customer customer=(Customer)customers.next();
5. customer.setAge(customer.getAge()+1);
6. }
7. tx.commit();
8. session.close();
如果CUSTOMERS表中有1萬條年齡大於零的記錄,那麼Session的find()方法會一下子載入1萬個Customer物件到記憶體。當執行tx.commit()方法時,會清理快取,Hibernate執行1萬條更新CUSTOMERS表的update語句:
1. update CUSTOMERS set AGE=? …. where ID=i;
2. update CUSTOMERS set AGE=? …. where ID=j;
3. update CUSTOMERS set AGE=? …. where ID=k;
以上Hibernate批量更新方式有兩個缺點:
(1) 佔用大量記憶體,必須把1萬個Customer物件先載入到記憶體,然後一一更新它們。
(2) 執行的update語句的數目太多,每個update語句只能更新一個Customer物件,必須通過1萬條update語句才能更新一萬個Customer物件,頻繁的訪問資料庫,會大大降低應用的效能。
為了迅速釋放1萬個Customer物件佔用的記憶體,可以在更新每個Customer物件後,就呼叫Session的evict()方法立即釋放它的記憶體:
1. tx = session.beginTransaction();
2. Iterator customers=session.find("from Customer c where c.age>0").iterator();
3. while(customers.hasNext()){
4. Customer customer=(Customer)customers.next();
5. customer.setAge(customer.getAge()+1);
6. session.flush();
7. session.evict(customer);
8. }
9. tx.commit();
10. session.close();
在 以上程式中,修改了一個Customer物件的age屬性後,就立即呼叫Session的flush()方法和evict()方法,flush()方法使 Hibernate立刻根據這個Customer物件的狀態變化同步更新資料庫,從而立即執行相關的update語句;evict()方法用於把這個 Customer物件從快取中清除出去,從而及時釋放它佔用的記憶體。
但evict()方法只能稍微提高批量操作的效能,因為不管有沒有使用evict()方法,Hibernate都必須執行1萬條update語句,才能更新1萬個Customer物件,這是影響批量操作效能的重要因素。假如Hibernate能直接執行如下SQL語句:
1. update CUSTOMERS set AGEAGE=AGE+1 where AGE>0;
那麼以上一條update語句就能更新CUSTOMERS表中的1萬條記錄。但是Hibernate並沒有直接提供執行這種update語句的介面。應用程式必須繞過Hibernate API,直接通過JDBC API來執行該SQL語句:
1. tx = session.beginTransaction();
2. Connection con=session.connection();
3. PreparedStatement stmt=con.prepareStatement("update CUSTOMERS set AGEAGE=AGE+1 "
4. +"where AGE>0 ");
5. stmt.executeUpdate();
6. tx.commit();
以 上程式演示了繞過Hibernate API,直接通過JDBC API訪問資料庫的過程。應用程式通過Session的connection()方法獲得該Session使用的資料庫連線,然後通過它建立 PreparedStatement物件並執行SQL語句。值得注意的是,應用程式仍然通過Hibernate的Transaction介面來宣告事務邊 界。
如果底層資料庫(如Oracle)支援儲存過程,也可以通過儲存過程來執行Hibernate批量更新。儲存過程直接在資料庫中執行,速度更加快。在Oracle資料庫中可以定義一個名為batchUpdateCustomer()的儲存過程,程式碼如下:
1. create or replace procedure batchUpdateCustomer(p_age in number) as
2. begin
3. update CUSTOMERS set AGEAGE=AGE+1 where AGE>p_age;
4. end;
以上儲存過程有一個引數p_age,代表客戶的年齡,應用程式可按照以下方式呼叫儲存過程:
1. tx = session.beginTransaction();
2. Connection con=session.connection();
3. String procedure = "{call batchUpdateCustomer(?) }";
4. CallableStatement cstmt = con.prepareCall(procedure);
5. cstmt.setInt(1,0); //把年齡引數設為0
6. cstmt.executeUpdate();
7. tx.commit();
從上面程式看出,應用程式也必須繞過Hibernate API,直接通過JDBC API來呼叫儲存過程。
Session的各種過載形式的update()方法都一次只能更新一個物件,而delete()方法的有些過載形式允許以HQL語句作為引數,例如:
1. session.delete("from Customer c where c.age>0");
如果CUSTOMERS表中有1萬條年齡大於零的記錄,那麼以上程式碼能刪除一萬條記錄。但是Session的delete()方法並沒有執行以下delete語句
1. delete from CUSTOMERS where AGE>0;
Session的delete()方法先通過以下select語句把1萬個Customer物件載入到記憶體中:
1. select * from CUSTOMERS where AGE>0;
接下來執行一萬條delete語句,逐個刪除Customer物件:
1. delete from CUSTOMERS where ID=i;
2. delete from CUSTOMERS where ID=j;
3. delete from CUSTOMERS where ID=k;
由 此可見,直接通過Hibernate API進行Hibernate批量更新和Hibernate批量刪除都不值得推薦。而直接通過JDBC API執行相關的SQL語句或呼叫相關的儲存過程,是Hibernate批量更新和Hibernate批量刪除的最佳方式,這兩種方式都有以下優點:
(1) 無需把資料庫中的大批量資料先載入到記憶體中,然後逐個更新或修改它們,因此不會消耗大量記憶體。
(2) 能在一條SQL語句中更新或刪除大批量的資料。