為什麼很多人不願意用hibernate了?

欲速則不達,欲達則欲速!

一、hibernate優勢

hibernate讓你不用寫sql了,這不單可以讓你的應用更好移植其它資料庫,更主要的是讓程式設計師更專注業務邏輯、資料關係、物件關係等。hibernate對一對多,多對多關係實現是非常好的。很關鍵一點,它支援lazy,可以讓你的資料只在需要的時候被載入,聽起來很完美。hibernate還有一個更牛的就是HQL,這是完全可以把查詢對映到你OO模型的查詢語言,和mybatis的對映比起來,還是更方便和更強大的。

1、@Lazy註解是什麼?

@Lazy註解用於標識bean是否需要延遲載入,原始碼如下:

@Target({ElementType。TYPE, ElementType。METHOD, ElementType。CONSTRUCTOR, ElementType。PARAMETER, ElementType。FIELD}) @Retention(RetentionPolicy。RUNTIME)@Documented public @interface Lazy { /** * Whether lazy initialization should occur。 */ boolean value() default true; }

只有一個引數,預設是true,也就是說只要加了這個註解就會延遲載入。

2、@Lazy註解怎麼使用

沒加註解之前主要容器啟動就會例項化bean,如下:

AnnotationConfigApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainConfig。class);

建立user例項

而加上@Lazy註解則必須在第一次呼叫的時候才會載入如下:

@Scope @Lazy @Bean(value=“user0”,name=“user0”,initMethod=“initUser”,destroyMethod=“destroyUser”) public User getUser(){ System。out。println(“建立user例項”); return new User(“張三”,26); }

AnnotationConfigApplicationContext applicationContext2 = new AnnotationConfigApplicationContext(MainConfig。class); User bean2 = applicationContext2。getBean(User。class);

建立user例項 例項1 === User [userName=張三, age=26]

@Lazy註解註解的作用主要是減少springIOC容器啟動的載入時間

二、hibernate劣勢

看完優勢之後,感覺hibernate無所不能了,無敵是多麼的寂寞。處理大量資料或者大併發情況的網路服務感覺不是很好用,那麼現在開始說說hibernate的問題。

1、難以使用資料庫的一些功能

hibernate將資料庫與開發者隔離了,開發者不需要關注資料庫是Oracle還是MySQL,hibernate來幫你生成查詢的sql語句,但問題來了,如果你想用某種資料庫特有的功能,或者要讓查詢的sql完全符合你的心意,這就難了。如果使用hibernate,雖然它能對生成的查詢進行一定程式的定製,但就有點隔靴撓癢的感覺了,而且你開發起來付出的代價更大。至於hibernate對native sql的支援,還是挺完善的,這種native sql還能返回non-managed entity,不走hibernate的cache,最佳化是搞定了,但如果整個專案都這麼整,那還是用mybatis吧。

很多時候,我們都有一個需求:得到資料庫伺服器的當前時間。這是因為本機時間和伺服器時間是有差別的。各種資料庫都提供了函式來獲得,比如,mysql,可以用“select now()”。hibernate也提供了一個函式current_timestamp(說起timestamp,個人認為資料庫的timestamp做的很差,它居然和datetime是一個數量級的(精確度),這怎麼可以用來表示真正的stamp啊!)。可是,你卻無法用直接使用“select current_timestamp()”來獲得伺服器的當前時間,你還必須加上一個查詢的表!比如,“select current_timestamp() from tbl_Good”。個人十分鬱悶,我只是想用這個簡單功能而已,為什麼我一定要知道資料庫裡面的表格呢????更何況還必須建立對映……

不是我不明白,這世界太複雜了 。每樣產品都是拼命的複雜化,其實,它們實在是忽略了一般的使用者只需要一小部分功能而已。預設的功能應該是能夠滿足普通使用者的常見需求的,那樣才算是一個好的產品。我不認為hibernate做到了這點。

2、滿足不了程式對cache的需求

很多web服務,對cache的依賴是非常大的,hibernate自帶的cache按理說也是很強大的,但還是滿足不了很多需求。

3、耦合度高

hibernate的確是在你專案開發的時候節約了很多時間,但是它對你的業務邏輯模型和資料庫模型互相依賴的程式太高了。短期沒啥問題,但隨著專案的變遷,這些都會改變,在維持這種僅僅耦合的關係的時候,你會發信你的程式碼特別脆弱,隨便改一處資料庫的schema,整個java專案可能要改幾十次。而且現在mybatis的自動mapping做的也很好,開發起來也沒花多長時間,等專案進入中後期,你需要大量定製和最佳化查詢的時候,mybatis的開發效率就更明顯了。

4、debug難

作為一個後端程式設計師,我比較喜歡每一行程式碼我都精確知道它到底在幹什麼。尤其是資料庫訪問的程式碼,往往系統的瓶頸就在這些地方產生,每一行都不能小看。我看過hibernate早期版本的部分程式碼,比我想象的複雜和強大很多。的確很多地方Hibernate可以強大的只用一行程式碼解決很多問題,但比如說一個update()或者save()到底做了什麼,這裡既有hibernate本身的邏輯,也有你應用的邏輯,如果這一行產生了問題,你該如何去做?我個人覺得這是很難搞的,還不如一開始費點事,用mybatis這種。

作為一個程式設計師,我始終堅持認為改程式碼比改配置檔案容易。

5、hibernate更新大批次資料

(1)hibernate批次更新customers表中大於零的所有記錄的age欄位:

Transaction transaction = session。beginTransaction(); Iterator customers=session。find(“from Customer c where c。age>0”)。iterator(); while(customers。hasNext()){ Customer customer=(Customer)customers。next(); customer。setAge(customer。getAge()+1); } transaction。commit(); session。close();

如果customers表中有一萬條年齡大於零的記錄,那麼session的find()方法會一下子載入一萬個customer物件到記憶體中。當執行tx。commit()方法時,會清理快取,hibernate執行一萬條更新customers表的update語句:

update CUSTOMERS set AGE=? …。 where ID=i;

(2)以上hibernate批次更新方式有兩個缺點

佔用大量記憶體空間,必須把一萬個customer物件先載入到記憶體,然後一一更新他們。

執行的update語句的數目太多,每個update語句只能更新一個Customer物件,必須透過1萬條update語句才能更新一萬個Customer物件,頻繁的訪問資料庫,會大大降低應用的效能。

(3)為了迅速釋放1萬個Customer物件佔用的記憶體,可以在更新每個Customer物件後,就呼叫Session的evict()方法立即釋放它的記憶體:

Transaction transaction = session。beginTransaction(); Iterator customers=session。find(“from Customer c where c。age>0”)。iterator(); while(customers。hasNext()){ Customer customer=(Customer)customers。next(); customer。setAge(customer。getAge()+1); session。flush(); session。evict(customer); } transaction。commit(); session。close();

在以上程式中,修改了一個Customer物件的age屬性後,就立即呼叫Session的flush()方法和evict()方法,flush()方法使hibernate立刻根據這個Customer物件的狀態變化同步更新資料庫,從而立即執行相關的update()語句;evict()方法用於把這個Customer物件從快取中清除出去,從而及時釋放它佔用的記憶體。

但evict()方法只能稍微提高批次操作的效能,因為不管有沒有使用evict()方法,Hibernate都必須執行1萬條update語句,才能更新1萬個Customer物件,這是影響批次操作效能的重要因素。假如Hibernate能直接執行如下SQL語句:

update CUSTOMERS set AGEAGE=AGE+1 where AGE>0;

那麼以上一條update語句就能更新CUSTOMERS表中的1萬條記錄。但是Hibernate並沒有直接提供執行這種update語句的介面。應用程式必須繞過Hibernate API,直接透過JDBC API來執行該SQL語句:

Transaction transaction = session。beginTransaction(); Connection con=session。connection(); PreparedStatement stmt=con。prepareStatement(“update CUSTOMERS set AGEAGE=AGE+1 where AGE>0 ”); stmt。executeUpdate(); transaction。commit();

以上程式演示了繞過Hibernate API,直接透過JDBC API訪問資料庫的過程。應用程式透過Session的connection()方法獲得該Session使用的資料庫連線,然後透過它建立 PreparedStatement物件並執行SQL語句。值得注意的是,應用程式仍然透過Hibernate的Transaction介面來宣告事務邊 界。

如果底層資料庫(如Oracle)支援儲存過程,也可以透過儲存過程來執行Hibernate批次更新。儲存過程直接在資料庫中執行,速度更加快。在Oracle資料庫中可以定義一個名為batchUpdateCustomer()的儲存過程,程式碼如下:

create or replace procedure batchUpdateCustomer(p_age in number) as begin update CUSTOMERS set AGEAGE=AGE+1 where AGE>p_age; end;

以上儲存過程有一個引數p_age,代表客戶的年齡,應用程式可按照以下方式呼叫儲存過程:

Transaction transaction = session。beginTransaction(); Connection con=session。connection(); String procedure = “{call batchUpdateCustomer(?) }”; CallableStatement cstmt = con。prepareCall(procedure); cstmt。setInt(1,0); //把年齡引數設為0 cstmt。executeUpdate(); transaction。commit();

從上面程式看出,應用程式也必須繞過Hibernate API,直接透過JDBC API來呼叫儲存過程。

6、hibernate刪除大批次資料

Session的各種過載形式的update()方法都一次只能更新一個物件,而delete()方法的有些過載形式允許以HQL語句作為引數,例如:

session。delete(“from Customer c where c。age>0”);

如果CUSTOMERS表中有1萬條年齡大於零的記錄,那麼以上程式碼能刪除一萬條記錄。但是Session的delete()方法並沒有執行以下delete語句

delete from CUSTOMERS where AGE>0;

Session的delete()方法先透過以下select語句把1萬個Customer物件載入到記憶體中:

select * from CUSTOMERS where AGE>0;

接下來執行一萬條delete語句,逐個刪除Customer物件:

delete from CUSTOMERS where ID=i; delete from CUSTOMERS where ID=j; delete from CUSTOMERS where ID=k;

由 此可見,直接透過Hibernate API進行Hibernate批次更新和Hibernate批次刪除都不值得推薦。而直接透過JDBC API執行相關的SQL語句或呼叫儲存過程,是hibernate批次更新和批次刪除的最佳方式。

轉自:https://zhuanlan。zhihu。com/p/103120396?utm_source=qq