轉(zhuǎn)載自 微信公眾號 碼農(nóng)翻身 不用于商業(yè)宣傳 版權(quán)歸原作者所有 侵權(quán)刪
1 抱怨
JDBC出現(xiàn)以后佩谣, 以其對數(shù)據(jù)庫訪問出色的抽象绽昏, 良好的封裝扬霜, 特別是把接口和具體分開的做法, 贏得了一片稱贊而涉。
(碼農(nóng)翻身注: 參見文章《JDBC的誕生》)
乘著Java和互聯(lián)網(wǎng)的東風著瓶, JDBC在風口飛了起來, 無數(shù)的程序開始使用JDBC進行編程啼县, 訪問數(shù)據(jù)庫村的數(shù)據(jù)庫材原, 在數(shù)據(jù)庫村,無論是大佬Oracle, 還是小弟Mysql都賺的盆滿缽滿季眷。
所謂物極必反余蟹, JDBC的代碼寫得多了, 它的弱點就暴露出來了子刮, 很多碼農(nóng)抱怨道:“JDBC是在是太Low了”威酒。
消息傳到Tomcat村, Java 簡直不相信自己的耳朵: “什么挺峡? JDBC還很low ? 看來那幫碼農(nóng)沒有用socket訪問過數(shù)據(jù)庫吧葵孤?! 那才叫l(wèi)ow . ”
Tomcat 說 : “你是個標準的制定者橱赠, 寫代碼太少了尤仍,太不接地氣了, 你看看這樣的代碼: ”
Java 把代碼拿過來一看狭姨, 不由的倒吸了一口涼氣: “ 代碼怎么這么長啊宰啦, 似乎是有那么一點問題, ‘噪聲’似乎太多了饼拍, 把業(yè)務(wù)代碼全給淹沒了”
Tomcat說:”看來你也是個明白人啊赡模, 為了正確的打開和關(guān)閉你定義的Connection , Statement, ResultSet 需要花很多功夫, 再加上那些異常處理师抄, 一個50多行的程序漓柑, 真正做事的也就那么10幾行而已, 這些瑣碎代碼太煩人了司澎, 所以大家抱怨很low啊欺缘。 ”
Java表示同意: “不錯栋豫, 可以想象挤安, 如果代碼中有大量這樣的代碼, 碼農(nóng)會抓狂的丧鸯, 不過蛤铜,” Java突然想到了些什么 , “其實這不是我的問題, 碼農(nóng)們抱怨錯人了围肥, 我作為一門語言剿干, 所能提供的就是貼近底層(socket)的抽象, 這樣通用性最強穆刻。 至于碼農(nóng)想消除這些重復代碼置尔, 完全可以再封裝, 再抽象氢伟, 再分層啊”
Tomcat想想也是榜轿, 在計算機世界里, 每個人都有分工朵锣, 不能強求別人做不喜歡也不擅長的事情谬盐, 看來這件事錯怪Java了。
2 JDBCTemplate
Java 預料的不錯诚些, 稍微有點追求飞傀, 不愿意寫重復代碼的碼農(nóng)都對JDBC做了封裝, 例如寫個DBUtil的工具類把打開數(shù)據(jù)庫連接诬烹, 發(fā)出查詢語句都封裝了起來砸烦。
碼農(nóng)的抱怨也漸漸平息了。
有一天绞吁, 有個叫JDBCTemplate的人來到了Tomcat村找到了Java , 他自稱是Rod Johnson派來專門用于解決JDBC問題的外冀, 他提供了一個優(yōu)雅而簡潔的解決方案。
(碼農(nóng)翻身注: Rod Johnson就是Spring 最初的作者)
JDBCTemplate說: “尊敬的Java先生掀泳, 感謝您發(fā)明了JDBC雪隧, 讓我們可以遠程訪問數(shù)據(jù)庫村, 您也聽說了不少對JDBC的抱怨吧员舵, 我的主人Rod Johnson 也抱怨過脑沿, 不過他在大量的編程實踐中總結(jié)了很多經(jīng)驗, 他認為數(shù)據(jù)庫訪問無外乎這幾件事情:
指定數(shù)據(jù)庫連接參數(shù)
打開數(shù)據(jù)庫連接
聲明SQL語句
預編譯并執(zhí)行SQL語句
遍歷查詢結(jié)果
處理每一次遍歷操作
處理拋出的任何異常
處理事務(wù)
關(guān)閉數(shù)據(jù)庫連接”
“我的主人認為” JDBCTemplate說马僻, “開發(fā)人員只需要完成黑體字工作就可以了庄拇,剩下的事情由我來辦“
“你們主人的總結(jié)能力很強, 把一個框架應(yīng)該做的事情和用戶應(yīng)該做的事情區(qū)分開了 ” Java說
JDBCTemplate 看到Java 態(tài)度不錯韭邓, 趕緊趁熱打鐵: “ 我給你看個例子:”
Java 和之前那個傳統(tǒng)的JDBC比較了一下措近, JDBCTemplate的方式的確是把注意力放到了業(yè)務(wù)層面: 只關(guān)注SQL查詢, 以及把ResultSet和 User業(yè)務(wù)類進行映射女淑, 至于如何打開/關(guān)閉Connection, 如何發(fā)出查詢瞭郑,JDBCTemplate在背后都給你悄悄的完成了, 完全不用碼農(nóng)去操心鸭你。
“你在JDBC上做了不錯的抽象和封裝” Java 說屈张, “但是我還不明白JDBCTemplate 怎么創(chuàng)建出來的”
“這很簡單擒权, 你可以直接把它new 出來, 當然new 出來的時候需要一個參數(shù)阁谆, 就是javax.sql. DataSource碳抄, 這也是你定義的一個標準接口啊”
"當然, 我主人Rod Johnson推薦結(jié)合Spring 來使用场绿, 可以輕松的把我‘注入’到各個你需要的地方去"剖效。
“明白了, 你們主人這是要推銷Spring 啊焰盗, 那是什么東西贱鄙? ”
“簡單的說,就是一個依賴注入和AOP框架姨谷, 功能強大又靈活逗宁。 具體的細節(jié)還得讓我主人親自來給您介紹了 ”
(碼農(nóng)翻身注: 參見《Spring 的本質(zhì)系列(1) -- 依賴注入》和《Spring 的本質(zhì)系列(1) -- AOP》)
“好吧, 不管如何梦湘, 我看你用起來還不錯瞎颗, 可以向大家推薦一下“埔椋”
3 O/R Mapping
JDBCTemplate這樣對JDBC的封裝 哼拔, 把數(shù)據(jù)庫的訪問向前推進了一大步, 但是Tomcat村和數(shù)據(jù)庫村的很多有識之士都意識到: 本質(zhì)的問題仍然沒有解決瓣颅!
這個問題就是面向?qū)ο笫澜绾完P(guān)系數(shù)據(jù)世界之間存在的巨大鴻溝倦逐。
Tomcat村的Java 程序都是面向?qū)ο蟮模?封裝、繼承宫补、多態(tài)檬姥, 對象被創(chuàng)建起來以后, 互相關(guān)聯(lián)粉怕, 在內(nèi)存中形成了一張圖健民。
數(shù)據(jù)庫村的關(guān)系數(shù)據(jù)庫則是表格: 主鍵,外鍵贫贝, 關(guān)系運算秉犹、范式、事務(wù)稚晚。 數(shù)據(jù)被持久化在硬盤上崇堵。
ResultSet依然是對一個表的數(shù)據(jù)的抽象和模擬: rs.next() 獲取下一行, rs.getXXX("XX") 訪問該行某一列客燕。
把關(guān)系數(shù)據(jù)轉(zhuǎn)化成Java對象的過程鸳劳, 仍然需要碼農(nóng)們寫大量代碼來完成。
現(xiàn)在碼農(nóng)的呼聲越來越高幸逆, 要把這個過程給自動化了棍辕。 他們的要求很清晰: 我們只想用面向?qū)ο蟮姆绞絹砭幊蹋? 我們告訴你Java 類暮现、屬性 和數(shù)據(jù)庫表还绘、字段之間的關(guān)系楚昭, 你能不能自動的把對數(shù)據(jù)庫的增刪改查給實現(xiàn)了 ?
他們還把這個訴求起了一個很洋氣的名稱: O/R Mapping (Object Relational Mapping)
Java 自然不敢怠慢拍顷, 趕緊召集Tomcat村和數(shù)據(jù)庫村開了一次會議抚太, 確定了這么幾個原則:
1. 數(shù)據(jù)庫的表映射為Java 的類(class)
2. 表中的行記錄映射為一個個Java 對象
3. 表中的列映射為Java 對象的屬性。
但是光有這幾個原則是遠遠不夠的昔案, 一旦涉及到實際編程尿贫, 細節(jié)會撲面而來:
1. Java類的粒度要精細的多, 有時候多個類合在一起才能映射到一張表
例如下面的例子踏揣, User類 的name屬性其實是也是一個類庆亡, 但在數(shù)據(jù)庫User 表中, firstName, middleName, lastName卻是在同一張表中的。
2. Java 的面向?qū)ο笥欣^承捞稿, 而數(shù)據(jù)庫都是關(guān)系數(shù)據(jù)又谋, 根本沒有繼承這回事!
這時候可選的策略就很多了娱局, 比如
(1) 把父類和子類分別映射到各自的Table 中彰亥, 數(shù)據(jù)會出現(xiàn)冗余
(2) 把父類的公共屬性放到一個Table中, 每個子類都映射到各自的Table中衰齐, 但是只存放子類自己的屬性任斋。子類和父類的表之間需要關(guān)聯(lián)。
(3) 干脆把父類和子類都映射到同一張Table中耻涛, 用一個字段(例如Type)來表明這一行記錄是什么類型废酷。
3. 對象的標識問題
Java中使用a==b 或者 a.equals(b) 來進行對象是否相等的判斷, 而數(shù)據(jù)庫則是另外一套:使用主鍵抹缕。
4. 對象的關(guān)聯(lián)問題
在Java 中锦积, 一個對象關(guān)聯(lián)到另外一個或者一組對象是在是太常見了, 雙向的關(guān)聯(lián)(也就是A 引用B , B反過來也引用了A )也時常出現(xiàn)歉嗓, 而在數(shù)據(jù)庫中定義關(guān)聯(lián)能用的手段只剩下外鍵和關(guān)聯(lián)表了丰介。
5. 數(shù)據(jù)導航
在OOP中, 多個對象組成了一張網(wǎng)鉴分, 順著網(wǎng)絡(luò)上的路徑哮幢, 可以輕松的從一個對象到達另外一個對象。 例如: City c = user.getAddress().getCity();
在關(guān)系數(shù)據(jù)庫中非得通過SQL查詢志珍, 表的連接等方式來實現(xiàn)不可橙垢。
6. 對象的狀態(tài)
在OOP中, 對象無非就是創(chuàng)建出來使用伦糯, 如果不用了柜某,就需要回收掉嗽元, 但是一旦扯上數(shù)據(jù)庫, 勢必要在編程中引入新的狀態(tài)喂击,例如“已經(jīng)持久化”
......
(碼農(nóng)翻身注: 本來想講Hibernate, 但是限于篇幅剂癌, 實在是無法展開講細節(jié), 這幾個問題是Hibernate 官網(wǎng)上提到的翰绊, 是O/R Maping 的本質(zhì)問題)
這些細節(jié)問題讓Java 頭大佩谷, 他暗自思忖: " 還是別管那些碼農(nóng)的抱怨, 我還是守住JDBC這一畝三分地吧监嗜, 這些煩人的O/R Mapping 問題還是讓別人去處理好了谐檀。 "
O/R Mapping 的具體實現(xiàn)就這么被Java 擱置下了。
4 Hibernate 和 JPA
隨著時間的推移裁奇,各大廠商都想利用Java 賺錢桐猬, 聯(lián)合起來搞了一個叫J2EE的規(guī)范, 然后瘋狂的推行自己的應(yīng)用服務(wù)器(例如Weblogic, Websphere等等)刽肠,還搭配著硬件銷售溃肪, 在互聯(lián)網(wǎng)泡沫時代賺了不少錢。
J2EE中也有一個叫Entity Bean 的東西五垮, 試圖去搞定O/R Mapping , 但其蹩腳的實現(xiàn)方式被碼農(nóng)們罵了個狗血噴頭乍惊。
轉(zhuǎn)眼間, 時間到了2001年放仗, Tomcat告訴Java 說: “聽說了嗎润绎? 現(xiàn)在很多碼農(nóng)都被一個叫Hibernate的東西給迷住了”
"冬眠(Hibernate) ? 讓內(nèi)存中的數(shù)據(jù)在數(shù)據(jù)庫里冬眠诞挨, 這個名字起的很有意境啊 莉撇, 我猜是一個O/R Mapping 工具吧"
"沒錯, 是由一個叫Gavin King的小帥哥開發(fā)的惶傻, 這個框架很強悍棍郎, 它實現(xiàn)了我們之前討論的各種煩人細節(jié), 大家都趨之若鶩银室, 已經(jīng)成為O/R Mapping 事實上的標準涂佃, Entity Bean 已經(jīng)快被大家拋棄了。 "
“沒關(guān)系蜈敢, Entity Bean 從1.0 開始就是一個扶不起的阿斗辜荠, 我已經(jīng)想通了, 我這里只是指定標準抓狭, 具體的實現(xiàn)讓別人去做伯病。 既然Hibernate 這么火爆, 我們就把Gavin King 招安了吧 ”
“怎么招安否过? ”
“讓小帥哥過來領(lǐng)導著大家搞一個規(guī)范吧午笛, 參考一下Hibernate的成功經(jīng)驗 惭蟋, 他應(yīng)該會很樂意的。 ”
不久以后药磺, 一個新的Java規(guī)范誕生了告组, 專門用于處理Java 對象的持久化問題, 這個新的規(guī)范就是JPA(Java Persistence API), Hibernate 自然也實現(xiàn)了這個規(guī)范与涡, 幾乎就是JPA的首選了惹谐。
“碼農(nóng)翻身” 公共號 : 由工作15年的前IBM架構(gòu)師創(chuàng)建持偏,分享編程和職場的經(jīng)驗教訓驼卖。
長按二維碼, 關(guān)注碼農(nóng)翻身