關於SQL和ORM的爭論,永遠都不會終止,我也一直在思考這個問題。最近溫習了一遍SSH框架,發了動彈,和廣大猿友進行了深入的探討,被噴的五體投地,感慨萬千,因而就有了今天這篇文章。java
聲明:本文只是小編的一點拙見,不喜勿噴。mysql
欲速則不達,欲達則欲速!程序員
1、hibernate優點web
hibernate讓你不用寫sql了,這不單可讓你的應用更好移植其它數據庫,更主要的是讓程序員更專一業務邏輯、數據關係、對象關係等。hibernate對一對多,多對多關係實現是很是好的。很關鍵一點,它支持lazy,可讓你的數據只在須要的時候被加載,聽起來很完美。hibernate還有一個更牛的就是HQL,這是徹底能夠把查詢映射到你OO模型的查詢語言,和mybatis的映射比起來,仍是更方便和更強大的。spring
一、@Lazy註解是什麼?sql
@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,也就是說只要加了這個註解就會延遲加載。後端
二、@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容器啓動的加載時間
2、hibernate劣勢
看完優點以後,感受hibernate無所不能了,無敵是多麼的寂寞。處理大量數據或者大併發狀況的網絡服務感受不是很好用,那麼如今開始說說hibernate的問題。
一、難以使用數據庫的一些功能
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作到了這點。
二、知足不了程序對cache的需求
不少web服務,對cache的依賴是很是大的,hibernate自帶的cache按理說也是很強大的,但仍是知足不了不少需求。
三、耦合度高
hibernate的確是在你項目開發的時候節約了不少時間,可是它對你的業務邏輯模型和數據庫模型互相依賴的程序過高了。短時間沒啥問題,但隨着項目的變遷,這些都會改變,在維持這種僅僅耦合的關係的時候,你會發信你的代碼特別脆弱,隨便改一處數據庫的schema,整個java項目可能要改幾十次。並且如今mybatis的自動mapping作的也很好,開發起來也沒花多長時間,等項目進入中後期,你須要大量定製和優化查詢的時候,mybatis的開發效率就更明顯了。
四、debug難
做爲一個後端程序員,我比較喜歡每一行代碼我都精確知道它到底在幹什麼。尤爲是數據庫訪問的代碼,每每系統的瓶頸就在這些地方產生,每一行都不能小看。我看過hibernate早期版本的部分代碼,比我想象的複雜和強大不少。的確不少地方Hibernate能夠強大的只用一行代碼解決不少問題,但好比說一個update()或者save()到底作了什麼,這裏既有hibernate自己的邏輯,也有你應用的邏輯,若是這一行產生了問題,你該如何去作?我我的以爲這是很難搞的,還不如一開始費點事,用mybatis這種。
做爲一個程序員,我始終堅持認爲改代碼比改配置文件容易。
五、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批量更新方式有兩個缺點
(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來調用存儲過程。
六、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批量更新和批量刪除的最佳方式。
3、羣衆的眼光的雪亮的,千萬不要逆天而行
4、被噴以後的一些感悟
感受就是一場批鬥大會,我深深的感受到才疏學淺的無奈,我真的只是想好好學習而已,但願若干年後,我還能初心不改。
做爲一個初級程序員而言,沒有必要花費過多的時間去證實技術的無用論,我並無對hibernate這個框架進行深刻的研究,只是在膚淺的層面的一些我的感悟。
框架自己並無對錯一說,只有適合不適合,任何框架都有其自身的能力範圍,hibernate封裝了不少有用的API給咱們,下降了操做數據庫的難度和複雜度,同時也減小了模板代碼的數量,但hibernate留給開發者的可操做空間相對mybatis少了不少;mybatis框架使用起來更加靈活,開發者能夠自定義查詢語句,但增長了模板代碼量,看起來並無hibernate邊界。兩種框架在便捷與靈活兩個指標上作出了取捨和妥協,這不能說是框架的錯。對於一個框架而言,須要有自身專一的領域和設計願景,不可能面面俱到,就如這位大哥所言:
使用任何一種技術框架,都須要貼合現實的業務需求以及自身的技術能力。當你尚未深刻的去了解一門技術或者當前業務需求沒法與框架契合時,不要盲目的批判框架的好壞。
相關博文