首頁 > 曲藝

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

作者:由 做一個程式設計俗人 發表于 曲藝日期:2023-02-03

hibernate被淘汰了嗎

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

【推薦】2019 Java 開發者跳槽指南。pdf(吐血整理) >>>

關於SQL和ORM的爭論,永遠都不會終止,我也一直在思考這個問題。最近溫習了一遍SSH框架,發了動彈,和廣大猿友進行了深刻的探討,被噴的五體投地,感慨萬千,於是就有了今天這篇文章。

宣告:本文只是小編的一點拙見,不喜勿噴。

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

一、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)@Documentedpublic @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批次更新和批次刪除的最佳方式。

素小暖講Spring JdbcTemplate

三、群眾的眼光的雪亮的,千萬不要逆天而行

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

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

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

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

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

四、被噴之後的一些感悟

感覺就是一場批鬥大會,我深深的感覺到才疏學淺的無奈,我真的只是想好好學習而已,希望若干年後,我還能初心不改。

作為一個初級程式設計師而言,沒有必要花費過多的時間去證明技術的無用論,我並沒有對hibernate這個框架進行深入的研究,只是在膚淺的層面的一些個人感悟。

框架本身並沒有對錯一說,只有適合不適合,任何框架都有其自身的能力範圍,hibernate封裝了很多有用的API給我們,降低了操作資料庫的難度和複雜度,同時也減少了模板程式碼的數量,但hibernate留給開發者的可操作空間相對mybatis少了很多;mybatis框架使用起來更加靈活,開發者可以自定義查詢語句,但增加了模板程式碼量,看起來並沒有hibernate邊界。兩種框架在便捷與靈活兩個指標上做出了取捨和妥協,這不能說是框架的錯。對於一個框架而言,需要有自身專注的領域和設計願景,不可能面面俱到,就如這位大哥所言:

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

使用任何一種技術框架,都需要貼合現實的業務需求以及自身的技術能力。當你還沒有深入的去了解一門技術或者當前業務需求無法與框架契合時,不要盲目的批判框架的好壞。

相關博文

素小暖講Java框架(SSH、SSM、Springboot)

本文轉載自:https://my。oschina。net/u/4006148/blog/3157757,旨在分享技術,整合資訊,若有侵權請私信,侵權必刪