轉(zhuǎn)載自 微信公眾號 碼農(nóng)翻身 不用于商業(yè)宣傳 版權(quán)歸原作者所有 侵權(quán)刪
網(wǎng)絡(luò)訪問
隨著 Oracle, Sybase, SQL Server ,DB2, Mysql 等人陸陸續(xù)續(xù)住進數(shù)據(jù)庫村, 這里呈現(xiàn)出一片興旺發(fā)達的景象, 無數(shù)的程序在村里忙忙碌碌, 讀寫數(shù)據(jù)庫贷帮, 實際上一個村落已經(jīng)容不下這么多人了, 數(shù)據(jù)庫村變成了數(shù)據(jù)鎮(zhèn)扑媚。
這一天曾我, 數(shù)據(jù)庫鎮(zhèn)發(fā)生了一件大事: 它連上了網(wǎng)絡(luò)勤家!
外部的花花世界一下全部打開圾浅, 很多程序開始離開這個擁擠的城鎮(zhèn)掠手, 住到更加宜居的地方去。
可是他們的工作還是要讀寫數(shù)據(jù)庫贱傀, 大家都在想辦法能不能通過網(wǎng)絡(luò)來訪問數(shù)據(jù)庫鎮(zhèn)的數(shù)據(jù)庫惨撇。
其中移居到Tomcat村的Java 最為活躍, 這小子先去拜訪了一下Mysql , 相對于Oracle, Sybase 等大佬府寒, Mysql 還很弱小, 也許容易搞定报腔。
Java 說: “Mysql 先生株搔, 現(xiàn)在已經(jīng)網(wǎng)絡(luò)時代了, 您也得與時俱進啊纯蛾, 給我們開放下網(wǎng)絡(luò)接口吧纤房。 ”
Mysql 說: “還網(wǎng)絡(luò)時代, 你們這些家伙越來越懶了翻诉, 都不愿意到我們家里來了炮姨! 說說吧, 你想怎么開放碰煌? ”
Java 說: “很簡單舒岸, 您聽說過TCP/IP還有Socket 沒有? 沒有嗎芦圾?蛾派! 沒關(guān)系, 您的操作系統(tǒng)肯定知道, 它內(nèi)置實現(xiàn)了TCP/IP和socket, 您只需要和他商量一下洪乍, 需要申請一個ip 眯杏, 確定一個端口, 然后您在這個端口監(jiān)聽壳澳, 我每次想訪問數(shù)據(jù)了岂贩, 就會創(chuàng)建一個socket ,向你發(fā)起連接請求巷波, 你接受了就行了河闰。 ”
(劉欣注: 參見《張大胖的socket》)
“這么麻煩啊褥紫?”
“其實也簡單姜性, 您的操作系統(tǒng)會幫忙的, 他非常熟悉髓考, 再說只需要做一次就行部念, 把這個網(wǎng)絡(luò)訪問建立起來, 到時候很多程序都會來訪問您氨菇, 您會發(fā)財?shù)摹?”
“不會這么簡單吧儡炼, 假設(shè)說, 我是說假設(shè)啊查蓉, 通過socket我們建立了連接乌询, 通過這個連接, 你給我發(fā)送什么東西豌研? 我又給你發(fā)什么東西妹田?” Mysql非常老練, 直擊命門鹃共。
“呃鬼佣, 這個.... ”
Java 其實心里其實非常明白, 這需要和Mysql定義一個應用層的協(xié)議霜浴, 就是所謂的你發(fā)什么請求晶衷, 我給你什么響應。
例如:
客戶端程序先給Mysql 打個招呼阴孟, Mysql也回應一下晌纫, 俗稱握手。
怎么做認證永丝、授權(quán)锹漱, 數(shù)據(jù)加密, 數(shù)據(jù)包分組类溢。
用什么格式發(fā)送查詢語句凌蔬, 用什么格式來發(fā)送結(jié)果露懒。
如果結(jié)果集很大, 要一下子全發(fā)過來嗎砂心?
怎么做數(shù)據(jù)緩沖懈词?
......
等等一系列讓人頭痛的問題。
本來Java是想獨自定義辩诞, 這樣自己也許能占點便宜坎弯, 沒想到Mysql 直接提出來了。
“這樣吧 ” Java 說 “我們先把這個應用層的協(xié)議定義下來译暂, 然后您去找操作系統(tǒng)來搞定socket如何抠忘? ”
“這還差不多 ” 。 Mysql 同意了外永。
兩人忙活了一星期崎脉, 才把這個應用層協(xié)議給定義好。
然后又忙了一星期伯顶, 才把Mysql 這里的socket搞定囚灼。
Java 趕緊回到Tomcat村, 做了一個實驗: 通過socket和mysql 建立連接祭衩, 然后通過socket 發(fā)送約定好的應用層協(xié)議灶体, 還真不錯, 一次都調(diào)通了掐暮, 看來準備工作很重要啊蝎抽。
(劉欣注: 這是我的杜撰, mysql 的網(wǎng)絡(luò)訪問早就有了路克, 并不是java 捷足先登搞出來的)
統(tǒng)一接口
搞定了Mysql , Java 很得意樟结, 這是一個很好的起點, 以后和Oracle, SQL Server, Db2等大佬談判也有底氣了衷戈。
尤其是和mysql 商量出的應用層協(xié)議狭吼, mysql 也大度的公開了, 這樣一來殖妇, 不管是什么語言寫的程序,管你是java, pyhton, ruby , php...... 只要能使用socket, 就可以遵照mysql 的應用層協(xié)議進行訪問破花, mysql 的顧客呈指數(shù)級增長谦趣, 財源滾滾。 尤其是一個叫PHP的家伙座每, 簡直和mysql 成了死黨前鹅。
Oracle, Db2那幫大佬一看, 立刻就紅了眼峭梳, 不到Java 去談判舰绘, 也迫不及待的定義了一套屬于自己的應用層訪問協(xié)議蹂喻。
令人抓狂的是, 他們的網(wǎng)絡(luò)訪問協(xié)議和Msyql 的完全不一樣 捂寿! 這就意味著之前寫的針對mysql 的程序無法針對Oracle , Db2通用口四, 如果想切換數(shù)據(jù)庫, 每個程序都得另起爐灶寫一套代碼秦陋!
更讓人惡心的是蔓彩, 每套代碼都得處理非常多的協(xié)議細節(jié), 每個使用Java進行數(shù)據(jù)庫訪問的程序都在喋喋不休的抱怨: 我就想通過網(wǎng)絡(luò)給數(shù)據(jù)庫發(fā)送SQL語句, 怎么搞的這么麻煩驳概?
原因很簡單赤嚼, 就是直接使用socket編程, 太low 了 顺又, 必須得有一個抽象層屏蔽這些細節(jié)更卒!
Java 開始苦苦思索, 做出一個好的抽象不是那么容易的稚照。
首先得有一個叫連接(Connection)的東西蹂空, 用來代表和數(shù)據(jù)庫的連接。
想執(zhí)行SQL怎么辦锐锣? 用一個Statement來 表示吧腌闯。 SQL返回的結(jié)果也得有個抽象的概念: ResultSet 。
他們之間的關(guān)系如圖所示:
從Connection 可以創(chuàng)建Statement, Statement 執(zhí)行查詢可以得到ResultSet雕憔。
ResultSet提供了對數(shù)據(jù)進行遍歷的方法姿骏, 就是rs.next() , rs.getXXXX .... 完美斤彼!
對了分瘦, 無論是Connection, 還是Statement, ResultSet , 他們都應該是接口琉苇,而不能是具體實現(xiàn)嘲玫。
具體的實現(xiàn)需要由各個數(shù)據(jù)庫或者第三方來提供, 毫無疑問并扇, 具體的實現(xiàn)代碼中就需要處理那些煩人的細節(jié)了去团!
Java 把這個東西叫做JDBC, 想著自己定義了一個標準接口穷蛹, 把包袱都甩給你別人土陪, 他非常得意。
面向接口編程
第一個使用JDBC肴熏, 叫做學生信息管理的程序很快發(fā)現(xiàn)了問題鬼雀, 跑來質(zhì)問Java: “你這個Connection 接口設(shè)計的有問題!”
Java 說: “不可能蛙吏, 我的設(shè)計多完善霸戳ā鞋吉!”
“看來你這個規(guī)范的制定者沒有真正使用啊, 你看看励烦, 我想連接Mysql, 把Mysql 提供的jdbc實現(xiàn)(mysql-connector-java-4.1.jar )拿了過來谓着, 建立一個Connection : ”
“這不是挺正常的嗎? 你要連接Mysql , 肯定要提供ip地址崩侠, 端口號漆魔,數(shù)據(jù)庫名啊” Java 問到。
“問題不在這里却音, 昨天我遇到mysql了改抡, 他給了我一個號稱性能更強勁的升級版mysql-connector-java-5.0.jar, 我升級以后系瓢, 發(fā)現(xiàn)我的代碼編譯都通不過了阿纤, 原來mysql 把MysqlConnectionImpl 這個類名改成了 MysqlConnectionJDBC4Impl , 你看看夷陋, 你整天吹噓著要面向接口編程欠拾, 不要面向?qū)崿F(xiàn)編程, 但是你自己設(shè)計的東西都做不到啊”
Java覺得背上開始出汗骗绕, 那個程序說的沒錯藐窄, 設(shè)計上出了漏洞, 趕緊彌補吧酬土。
既然不能直接去 new 一個Connection 的實現(xiàn)荆忍, 肯定要通過一個新的抽象層來做, 這個中間層叫做什么撤缴?
Java 想到了電腦上的驅(qū)動程序刹枉, 很多硬件沒法直接使用, 除非安裝了驅(qū)動屈呕。 那我也模擬一下再做一個抽象層吧: Driver微宝。
每個數(shù)據(jù)庫都需要實現(xiàn)Driver 接口, 通過Driver 可以獲得數(shù)據(jù)庫連接Connection 虎眨, 但是這個Driver 怎么才能new 出來呢蟋软? 肯定不能直接new , Java似乎陷入了雞生蛋、蛋生雞的無限循環(huán)中了嗽桩。
最后钟鸵, 還是Java的反射機制救了他, 不要直接new 了涤躲, 每個數(shù)據(jù)庫的Driver 都用一個文本字符串來表示, 運行時動態(tài)的去裝載贡未, 例如mysql 的Driver 是這樣的:
Oracle是這樣的:
只要這個Driver Class不改動种樱, 其他具體的實現(xiàn)像Connection, Statement, ResultSet想怎么改就怎么改蒙袍。
接下來的問題是同一個程序可能訪問不同的數(shù)據(jù)庫, 可能有多個不同Driver 都被動態(tài)裝載進來嫩挤, 如何來管理害幅?
那就搞一個DriverManager吧, Mysql 的Driver, Oracle的Driver 在類初始化的時候岂昭, 一定得注冊到DriverManager中來以现, 這樣DriverManager才能管理啊:
注意: 關(guān)鍵點是static 的代碼塊约啊, 在一個類被裝載后就會執(zhí)行邑遏。
DriverManager 可以提供一個getConnection的方法, 用于建立數(shù)據(jù)庫Connection 恰矩。
DriverManager會把這些信息傳遞給每個Driver 记盒, 讓每個Driver去創(chuàng)建Connection 。
慢著外傅! 如果DriverManager 里既有MysqlDriver, 又有OracleDriver , 這里到底該連接哪一個數(shù)據(jù)庫呢纪吮? 難道讓兩個Driver 都嘗試一下? 那樣太費勁了吧萎胰, 還得區(qū)分開碾盟,沒法子只好讓那些程序在數(shù)據(jù)庫連接url中來指定吧:
url中指明了這是一個什么數(shù)據(jù)庫, 每個Driver 都需要判斷下是不是屬于自己支持的類型技竟, 是的話就開始連接冰肴, 不是的話直接放棄。
(劉欣注: 每個Driver接口的實現(xiàn)類都需要實現(xiàn)一個acceptsURL(Sting url)方法灵奖, 判斷這個url是不是自己能支持的嚼沿。 )
唉,真是不容易啊瓷患, Java想骡尽, 這下整個體系就完備了吧, 為了獲得一個Connection , 綜合起來其實就這么幾行代碼:
無論是任何數(shù)據(jù)庫擅编, 只要正確實現(xiàn)了Driver攀细, Connection 等接口, 就可以輕松的納入到JDBC框架下了爱态。
Java終于可以高興的宣布: “JDBC正式誕生了谭贪!”
(完)
“碼農(nóng)翻身” 公共號 : 由工作15年的前IBM架構(gòu)師創(chuàng)建,分享編程和職場的經(jīng)驗教訓锦担。
長按二維碼俭识, 關(guān)注碼農(nóng)翻身