姓名:盼厶В康 ?學號:17101223416
轉(zhuǎn)載自:https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513438&idx=1&sn=2967d595bb7d4ffdd2dacd3ab7501bbd&chksm=80d6799db7a1f08b27dc97650434fb2fc0e2570628945db99d9300a99e52828fd05c42fdb441&scene=38#wechat_redirect
【嵌牛導讀】:隨著 Oracle, Sybase, SQL Server ,DB2, ?Mysql 等人陸陸續(xù)續(xù)住進數(shù)據(jù)庫村, 這里呈現(xiàn)出一片興旺發(fā)達的景象摧茴, 無數(shù)的程序在村里忙忙碌碌哄褒, 讀寫數(shù)據(jù)庫稀蟋, 實際上一個村落已經(jīng)容不下這么多人了, 數(shù)據(jù)庫村變成了數(shù)據(jù)鎮(zhèn)呐赡。
【嵌牛鼻子】:JDBC
【嵌牛提問】:JDBC是怎么解決數(shù)據(jù)庫底層連接問題的呢退客?
【嵌牛正文】:
一、網(wǎng)絡(luò)訪問
這一天链嘀, 數(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ā)起連接請求饺蚊, 你接受了就行了。 ”
“這么麻煩靶ぁ卸勺?”
“其實也簡單, 您的操作系統(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)通了, ? 看來準備工作很重要啊誊垢。
二掉弛、統(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正式誕生了嫌变!”