標籤:

ORM是否必要?

本人學得是java,所以用了2年的hibernate。一開始跟潮流學習三大框架,現在覺得沒有必要使用hibernate,直接封裝一下apache dbutil就行了。我不知是否自己太淺薄,特來請教。


相比裸 sql 的話,能想到使用 orm 的好處主要是:

  • 默認的防注入,使用裸 sql 配合一些規範也可以避免注入,但是一旦不遵循規範仍是危險的;
  • 查詢條件的動態構造,比如我想根據用戶的不同參數來組合不同的查詢,使用 orm 只要在 query 對象里追加條件就可以,然而交給裸 sql 幾乎不可能幹凈又安全地做到;
  • 容易插鉤子集成自動的緩存失效(不過個人沒在線上見過);

聽到過的反對使用 orm 的意見似乎主要在於:

  • 開發者不能明確 sql 的內容,使 dba 有時候不好辦,反觀有限數量的裸 sql,dba 很容易 review;

個人傾向於這不算 orm 的鍋,開發者在使用 orm 構造查詢時應該理解 query 的內容,dba 也有自己的方法和流程去了解系統中存在的 query。


ORM可以防注入只是一個附加功能,拼SQL也同樣可以帶防注入機制啊。

ORM主要作用是把資料庫領域的東西映射到面向對象領域,因為開發者更熟悉。開發者在開發的時候更多是以對象方式去思考的,他們更熟悉user has many topics,而不是row、column、foreign key。面向對象之後帶來的好處就是業務層面的復用,包括關聯關係、業務邏輯。

上面說的是OLTP,OLAP可能情況就會不一樣了。用ORM來做OLAP分析就會有點違和感。雖然大部分ORM工具也提供group、join、sum等介面(ActiveRecord其實只提供了inner join,要用left join和cross join就必須拼SQL了),但實際上幫助不大,直接寫SQL是最簡單直接的。因為在OLAP應用,思考方式並不是面向對象的,而是面向主題,維度、度量才是核心。


工程上沒有絕對必要的東西,但是工程上說,ORM是極有價值的東西。當年也有人覺得 ORM 浪費資源,思路不清晰,雖然用了 Hibernate ,還是直接寫 SQL ,手工操作。果不其然被注入然後脫褲了。

當然這個名為 12306 的網站,你不爽你上網把它罵成狗,你還得用不是?換你自己的項目就此掛了也是完全可能的。

不作死,就不會死。駕馭它然後再討論它的弊端不遲。


誰有那張,堆棧調用的圖,一大堆的tomcat的、spring mvc的、jdbc的什麼什麼的,但只有一丁點是業務邏輯的?

orm和其它那些框架一樣,就是為了讓你從底層操作中解脫出來,去專心做業務邏輯的。

===================2017-09-21補充

最近重讀《企業應用架構模式》,又多了一點理解。對領域模型來說,由於領域對象和資料庫表是無法直接映射上的,因此必須有一個映射器;對錶模式(表驅動設計)來說,內存對象與資料庫表一一映射。

hibernate這種ORM,適用於後面這種模式。

myBatis的話,我傾向於它更適用於領域模型。因為就我自己的使用經驗來看,myBatis很少會「一一映射」,總會有些其他的映射。


ORM並不是為了防注入,不用ORM也可以不被注入。java的orm也不是只有hibernate一種可選。

關於為什麼要用orm,我覺得這個答案更合適:

What are the advantages of using an ORM?


不需要。參數都是作為 prepare statement 的參數送到伺服器,不存在拼接,自然就不存在注入。查詢條件可以用條件表達式來做。因為所有類型都有 null 值,可以用這個來表示某個條件是否生效。用 SQL 還能直接構造 json。json 之類複合類型還能 aggregate 成 array。有了這些,直接就能由 SQL 構造出一個複合對象出來,比起在宿主環境里構造,方便得多。


作為一個有著從sql程度員到ORM狂熱愛好者,然後又回歸到原始sql開發的程度員,我想描述一下我的歷程:

剛工作時,我們使用esql c以及4gl方式編寫資料庫應用(2000年前的事情,今天估計用的人很少了)。因為是第一份工作,沒有比較,也無法評述好與不好。

然後java出來,我算是第一代的java程度員,第一時間熱愛上了這個玩具(當時是jdk1.0,1.1的時代,連jdbc都沒有),我自己移植過rpc,實現過基於jni的資料庫訪問。2000年後,j2ee很快就成為行業高大尚的技術,ejb更是神一般的技術。當時我也加入到這個粉絲圈中,不為別的,既然是面向對象,當然應該釆用對象化的技術,思維來操作資料庫。(一個背景是基於jdbc編寫的資料庫應用實在是無比啰嗦)。當時還基於BCEL自己實現了一個容器外的類ejb的API,在我們自己的一個項目中進行了應用。

後來有了hibernate,也第一時間跟進,感概脫離了ejb容器的輕量以及第一時間得以閱讀一個完整的ORM實現源代碼,再回思相比自己曾經的原型實現,強了太多太多。也開始在項目中推廣hinernate。那段時間,不使用hibernate在java圈中都不好意思。比ejb1簡單好用多了。

不過,我開始懷念esql和4gl的時代,每當評審項目代碼,看到一大堆的問題時,我就在想,用這種方式寫代碼,完全沒有esql時代的簡單直接:

1. 程度員大都不了解資料庫的細節,看到的都是對象,連欄位的長度,底層類型,約束都不了解了。而我們當年,這一切瞭然於懷。(當然,這不是ORM的問題,但它確實讓程度員遠離了schema)

2.雖然ORM對簡單的資料庫操作可以很好實現,但一旦涉及到關聯查詢,修改,就複雜了,無論是ejb還是hibernate,都引入了自己的查詢語言。原諒我,每隔一段時間,我又要重查手冊才知道怎麼寫了

3. 本來是一行sql可以完成的代碼,orm後多了很多代碼,而且效率也大打折扣。

代碼評審多了,我就更加回憶起esql和4gl的好處來,我們現在做如此多的封裝,真的就簡化了嗎,這不就是一行或者幾行sql就可以完成的嗎?

大概是2009年,看到了groovy中的sql模式,當時有種回到了esql的感覺。大致了解後,開始在我們的系統中應用,幹掉hibernate,使用groovy sql來替代。由於groovy的弱類型性質和解釋執行,我們只使用它來編寫sql密集的代碼。

再後來,mybatis開始普及,某種程度也是sql的回歸。

現在,我們的團隊正在使用基於我開發的一個scala-sql(wangzaixiang/scala-sql 路 GitHub)的庫,(這個庫直接借鑒了groovy sql)回歸到最接近我剛工作時的esql模式,不再有容器,不再需要mapping,不再需要一個新的查詢語言,(對了,也不用擔心注入),也拋棄掉了DAO,服務中直接訪問資料庫,去除掉為了層次而層次的抽象。這一切當然要感謝scala語言的強大表達能力。

作為一個樣例,附一段代碼,不管你喜不喜歡,反正,現在最符合我的胃口:

def rejectToPurchase(): Unit = {
val orderItems = getOrderItemsByOrderId(entity.id)
val orderItemIds = orderItems.map(_.id)
val purchaseOrders = rows[PurchaseOrder](sql"SELECT po.* FROM purchase_order_item poi LEFT JOIN purchase_order po ON po.id=poi.purchase_order_id WHERE poi.order_item_id in " + buildSqlIn(orderItemIds))

//更新採購合同=新建
for (purchaseOrder &<- purchaseOrders) esql(sql""" update purchase_order set updated_by = ${ServiceContext.operatorId}, status = ${PurchaseOrderStatus.INIT} where id= ${purchaseOrder.id} """) esql(sql""" update orders set updated_by = ${ServiceContext.operatorId}, contract_flag = ${OrderContractFlag.UNCONFIRMED} where id= ${entity.id} """) }


對於大部分重業務邏輯的應用,其核心就是數據結構(資料庫表)設計,數據結構是根本的業務邏輯抽象。

ORM根本的意義在於把這個抽象結構化的暴露給應用代碼。當你直接訪問資料庫的時候,這個結構是存在與外部的,文檔裡面及程序員腦子裡的,在代碼裡面沒有物理上的東西對應。ORM把這個結構物理得提供給你,具體得說,ORM的時候你處理的是欄位對應關係,而這是顯然的,使用SQL直接操作的時候,你是用腦子把業務邏輯對應到SQL裡面的。還有一個,通過ORM,抽象被結構化、類型化得引入到應用代碼中,從而直接在代碼裡面實現了很多必要的約束,這對正確性有好處。


我要強行安利 Slick 這款 FRM……

自從用過 Slick 之後我就再也不用 JVM 上的 ORM 了…… 效率更高,冗餘更小,類型更安全,代碼更簡潔。


orm在工程上並不必須,題主可以試試jooq


國內那些開發者連個簡單開發都做不好,你不讓他們用ORM,他們分分鐘寫個能被注入的東西給你看。


ORM和注入有什麼關係,就算不用ORM就會被注入?

ORM主要是解決面向對象的設計方式和關係型資料庫之間的關聯

Java主要面向對象設計,因此在分析業務的時候會以對象的角度來看待問題。然而資料庫是關係型的,對於Java程序員而言是不符合面向對象設計的,因此才會出現ORM這種東西。

有了ORM,Java開發人員在整個代碼設計都將遵循對象的思維模式,這就是好處


ORM不是必要的,要不要用ORM取決於架構師的口味。Java-er習慣於過度設計,一丁點兒邏輯需要弄一堆文件再帶上百兆的庫不稀奇。

ORM最常解決的問題,就是對於簡單的CRUD操作(特別是單表操作,或簡單的關聯表查詢)以對象操作的方式而不是SQL方式來寫代碼。而複雜的操作,還是建議程序員自己來寫SQL。

從一個框架的角度,更好的設計,莫過於對於簡單的CRUD操作(包括關聯表查詢),什麼代碼都不用寫(框架自動解決);對於複雜的操作,程序員還是自己寫,框架提供最簡單的調用方式,比如一行代碼就可調用並獲取結果。


apache dbutil其實已經算是一種orm了,我之前做遊戲也是封裝一下dbutils,做好sql拼接,做好對象映射,要比hibernate要好用很多。我認為orm的意義在於我只能提供一些簡易的方法可以讓使用者簡單使用資料庫,減少手動出錯以及手寫sql帶來的安全問題。


ORM的優點是省事啊

不知道hibernate發展的如何,反正.net的EF爽到飛起,不論是先有模型/代碼再出SQL還是從舊庫中生成模型,不論是真外鍵還是代碼層面的模擬外鍵。存儲過程是麻煩了點,但也沒禁止我用啊不是?

防止注入倒是次要的,畢竟現在大都知道要用參數,沒多少人直接拼字元串了。

缺點是生成的SQL比較冗餘,效率低下(特別是複製的查詢),不過這點很少是問題,真成瓶頸了,優化手段也很多。


反正 .net 的 nhibernate 用起來非常酸爽,簡直辣雞。尤其各種4流程序員用了起來的時候 溜得飛起。。。。好像很牛逼似的,實際上 大大增加了開發的複雜性。

我個人傾向 Dapper 這種超輕量級的 ORM(比sqlhelper多不了太多功能但是確實少寫不少垃圾代碼) 加之以 AutoMapper 這樣的庫 用用還是可以的。

另外 我個人傾向於 模型污染 這個 概念,屁大的點業務 沒事 就建個模型,這種行為真是太猥瑣了,就算有AutoMapper 也是很噁心的一件事。


使用ORM,使用倉儲模式,一大堆複雜的架構設計。幾年前,剛從http://ADO.NET出來的時候,覺得好牛逼,有ORM這樣的高端貨。你仔細看下,用ORM的,每張表做操作都要去實現自己的介面,因為要通過類型去判斷操作那張表。還不如用Datatable,現在我又回歸到用原始SQL的情況,覺得還是用SQL好,不用反覆去寫一大堆代碼。


ORM相對於SQL的優缺點

相對來說,ORM的缺點就是SQL的優勢地方,而優點也是SQL的劣勢地方。

  • 優點
    • 方便的使用面向對象,語句清晰
    • 防注入『這個其實不算ORM的核心,因為比如Phalcon的SQL形式寫法也可以防注入』
    • 方便動態構造語句,對於不同的表的相同操作採用多態實現更優雅
    • 一定程度方便重構數據層『比如改表名,欄位名等』
    • 設置鉤子函數
  • 缺點
    • 不太容易處理複雜查詢語句
    • 性能較直接用SQL差

如何選擇

盡量使用ORM,除了含子查詢的複雜語句『不過大流量下的網站最好不要寫這種複雜SQL』,當然這也只是一個原則,所以反過來說也可以,不過以下幾個場景用ORM的好處是很明顯的

  • Model對象不確定的時候,使用多態的方式去處理不同實例的相同操作
  • 語句結構不確定的時候,比如根據不同的情況Where子句不一樣的時候,採用if的代碼結構去控制ORM方法的使用比拼接SQL語句要清晰的多
  • 設置鉤子函數,比如分頁裡面,拿分頁數據同時要count數據,那麼就可以在Model裡面插入這個算count的鉤子函數(包括緩存邏輯)

摘自個人博客ORM或者SQL


@劉鑫 他從工程這個角度切入來回答我認為是比較正確的。

摘一個百度百科的

面向對象是從軟體工程基本原則(如耦合、聚合、封裝)的基礎上發展起來的,而關係資料庫則是從數學理論發展而來的,兩套理論存在顯著的區別。為了解決這個不匹配的現象,對象關係映射技術應運而生

有需要愛解決這個現象的就應該需要啊,沒需要解決這個現象的,就不要啊


第一為了快速開發,第二可以對接多種類型資料庫,當項目大了更容易管理,如果你的項目比較小,完全可以不用


推薦閱讀:

項目里該不該用ORM?
在 PHP 領域裡,有哪些 ORM 比較好用?
為什麼很多人都喜歡 Django 的 ORM 而不是 SQLAlchemy,是因為簡單嗎?

TAG:SQL | Java | ORM |