码农翻身——JDBC的诞生
隨著 Oracle, Sybase, SQL Server ,DB2, ?Mysql 等人陸陸續(xù)續(xù)住進(jìn)數(shù)據(jù)庫村, 這里呈現(xiàn)出一片興旺發(fā)達(dá)的景象, 無數(shù)的程序在村里忙忙碌碌, 讀寫數(shù)據(jù)庫, ? 實(shí)際上一個(gè)村落已經(jīng)容不下這么多人了, 數(shù)據(jù)庫村變成了數(shù)據(jù)鎮(zhèn)。
?
這一天, 數(shù)據(jù)庫鎮(zhèn)發(fā)生了一件大事: 它連上了網(wǎng)絡(luò)!
?
外部的花花世界一下全部打開, ?很多程序開始離開這個(gè)擁擠的城鎮(zhèn), 住到更加宜居的地方去。
?
可是他們的工作還是要讀寫數(shù)據(jù)庫, 大家都在想辦法能不能通過網(wǎng)絡(luò)來訪問數(shù)據(jù)庫鎮(zhèn)的數(shù)據(jù)庫。
?
其中移居到Tomcat村的Java 最為活躍, 這小子先去拜訪了一下Mysql , ? ?相對(duì)于Oracle, Sybase 等大佬, Mysql 還很弱小, 也許容易搞定。
?
Java 說: “Mysql 先生, 現(xiàn)在已經(jīng)網(wǎng)絡(luò)時(shí)代了, 您也得與時(shí)俱進(jìn)啊, 給我們開放下網(wǎng)絡(luò)接口吧。 ”
?
Mysql ?說: “還網(wǎng)絡(luò)時(shí)代, 你們這些家伙越來越懶了, 都不愿意到我們家里來了! 說說吧, 你想怎么開放? ?”
?
Java 說: “很簡單, 您聽說過TCP/IP還有Socket 沒有? ? 沒有嗎?! ?沒關(guān)系, 您的操作系統(tǒng)肯定知道, 它內(nèi)置實(shí)現(xiàn)了TCP/IP和socket, ? 您只需要和他商量一下, 需要申請(qǐng)一個(gè)ip , 確定一個(gè)端口, 然后您在這個(gè)端口監(jiān)聽, ?我每次想訪問數(shù)據(jù)了, 就會(huì)創(chuàng)建一個(gè)socket ,向你發(fā)起連接請(qǐng)求, 你接受了就行了。 ”
“這么麻煩啊?”
?
“其實(shí)也簡單, 您的操作系統(tǒng)會(huì)幫忙的, 他非常熟悉, ?再說只需要做一次就行, 把這個(gè)網(wǎng)絡(luò)訪問建立起來, 到時(shí)候很多程序都會(huì)來訪問您, 您會(huì)發(fā)財(cái)?shù)摹?”
?
“不會(huì)這么簡單吧, 假設(shè)說, 我是說假設(shè)啊, ?通過socket我們建立了連接, 通過這個(gè)連接, 你給我發(fā)送什么東西? ?我又給你發(fā)什么東西?” ?Mysql非常老練, 直擊命門。
?
“呃, 這個(gè).... ?”
?
Java 其實(shí)心里其實(shí)非常明白, 這需要和Mysql定義一個(gè)應(yīng)用層的協(xié)議, 就是所謂的你發(fā)什么請(qǐng)求, 我給你什么響應(yīng)。 ?
?
例如:
客戶端程序先給Mysql 打個(gè)招呼, ?Mysql也回應(yīng)一下, 俗稱握手。
?
怎么做認(rèn)證、授權(quán), 數(shù)據(jù)加密, 數(shù)據(jù)包分組。
?
用什么格式發(fā)送查詢語句, ? 用什么格式來發(fā)送結(jié)果。
?
如果結(jié)果集很大, 要一下子全發(fā)過來嗎?
?
怎么做數(shù)據(jù)緩沖?
......
等等一系列讓人頭痛的問題。
?
本來Java是想獨(dú)自定義, 這樣自己也許能占點(diǎn)便宜, 沒想到Mysql ?直接提出來了。
?
“這樣吧 ” Java 說 “我們先把這個(gè)應(yīng)用層的協(xié)議定義下來, 然后您去找操作系統(tǒng)來搞定socket如何? ”
?
“這還差不多 ” 。 Mysql 同意了。
?
兩人忙活了一星期, 才把這個(gè)應(yīng)用層協(xié)議給定義好。
?
然后又忙了一星期, 才把Mysql 這里的socket搞定。
?
Java 趕緊回到Tomcat村, ?做了一個(gè)實(shí)驗(yàn): ?通過socket和mysql 建立連接, ?然后通過socket 發(fā)送約定好的應(yīng)用層協(xié)議, ? ?還真不錯(cuò), 一次都調(diào)通了, ? 看來準(zhǔn)備工作很重要啊。
?
(劉欣注: 這是我的杜撰, mysql 的網(wǎng)絡(luò)訪問早就有了, 并不是java 捷足先登搞出來的)
搞定了Mysql , ?Java 很得意, 這是一個(gè)很好的起點(diǎn), 以后和Oracle, SQL Server, Db2等大佬談判也有底氣了。
?
尤其是和mysql 商量出的應(yīng)用層協(xié)議, ? mysql 也大度的公開了, 這樣一來, 不管是什么語言寫的程序,管你是java, pyhton, ruby , php...... ? 只要能使用socket, ? 就可以遵照mysql 的應(yīng)用層協(xié)議進(jìn)行訪問, ?mysql 的顧客呈指數(shù)級(jí)增長, 財(cái)源滾滾。 ?尤其是一個(gè)叫PHP的家伙, 簡直和mysql 成了死黨。
?
Oracle, Db2那幫大佬一看, 立刻就紅了眼, 不到Java 去談判, 也迫不及待的定義了一套屬于自己的應(yīng)用層訪問協(xié)議。
?
令人抓狂的是, 他們的網(wǎng)絡(luò)訪問協(xié)議和Msyql 的完全不一樣 ! ?這就意味著之前寫的針對(duì)mysql 的程序無法針對(duì)Oracle , Db2通用, ?如果想切換數(shù)據(jù)庫, 每個(gè)程序都得另起爐灶寫一套代碼!
?
更讓人惡心的是, 每套代碼都得處理非常多的協(xié)議細(xì)節(jié), ? 每個(gè)使用Java進(jìn)行數(shù)據(jù)庫訪問的程序都在喋喋不休的抱怨: 我就想通過網(wǎng)絡(luò)給數(shù)據(jù)庫發(fā)送SQL語句, 怎么搞的這么麻煩?
?
原因很簡單, 就是直接使用socket編程, 太low 了 , ?必須得有一個(gè)抽象層屏蔽這些細(xì)節(jié)!
?
Java 開始苦苦思索, 做出一個(gè)好的抽象不是那么容易的。
?
首先得有一個(gè)叫連接(Connection)的東西, 用來代表和數(shù)據(jù)庫的連接。 ?
?
想執(zhí)行SQL怎么辦? 用一個(gè)Statement來 表示吧。 ? SQL返回的結(jié)果也得有個(gè)抽象的概念: ResultSet 。
他們之間的關(guān)系如圖所示:
從Connection 可以創(chuàng)建Statement, ? ?Statement 執(zhí)行查詢可以得到ResultSet。
?
ResultSet提供了對(duì)數(shù)據(jù)進(jìn)行遍歷的方法, 就是rs.next() , rs.getXXXX .... ? ? ?完美!
?
對(duì)了, 無論是Connection, 還是Statement, ResultSet , ?他們都應(yīng)該是接口,而不能是具體實(shí)現(xiàn)。
?
具體的實(shí)現(xiàn)需要由各個(gè)數(shù)據(jù)庫或者第三方來提供, 毫無疑問, 具體的實(shí)現(xiàn)代碼中就需要處理那些煩人的細(xì)節(jié)了!
?
Java 把這個(gè)東西叫做JDBC, ?想著自己定義了一個(gè)標(biāo)準(zhǔn)接口, 把包袱都甩給你別人, 他非常得意。
第一個(gè)使用JDBC, 叫做學(xué)生信息管理的程序很快發(fā)現(xiàn)了問題, ? 跑來質(zhì)問Java: “你這個(gè)Connection 接口設(shè)計(jì)的有問題!”
?
Java 說: “不可能, 我的設(shè)計(jì)多完善啊!”
?
“看來你這個(gè)規(guī)范的制定者沒有真正使用啊, ?你看看, 我想連接Mysql, 把Mysql 提供的jdbc實(shí)現(xiàn)(mysql-connector-java-4.1.jar?)拿了過來, ? 建立一個(gè)Connection : ”
“這不是挺正常的嗎? 你要連接Mysql , 肯定要提供ip地址, 端口號(hào),數(shù)據(jù)庫名啊” Java 問到。
?
“問題不在這里, 昨天我遇到mysql了, 他給了我一個(gè)號(hào)稱性能更強(qiáng)勁的升級(jí)版mysql-connector-java-5.0.jar, ?我升級(jí)以后, 發(fā)現(xiàn)我的代碼編譯都通不過了, ?原來mysql 把MysqlConnectionImpl 這個(gè)類名改成了 MysqlConnectionJDBC4Impl ?, 你看看, 你整天吹噓著要面向接口編程, 不要面向?qū)崿F(xiàn)編程, 但是你自己設(shè)計(jì)的東西都做不到啊”
?
Java覺得背上開始出汗, ?那個(gè)程序說的沒錯(cuò), ?設(shè)計(jì)上出了漏洞, 趕緊彌補(bǔ)吧。
?
既然不能直接去 new 一個(gè)Connection 的實(shí)現(xiàn), 肯定要通過一個(gè)新的抽象層來做, 這個(gè)中間層叫做什么?
?
Java 想到了電腦上的驅(qū)動(dòng)程序, 很多硬件沒法直接使用, 除非安裝了驅(qū)動(dòng)。 ? 那我也模擬一下再做一個(gè)抽象層吧:?Driver。
?
每個(gè)數(shù)據(jù)庫都需要實(shí)現(xiàn)Driver 接口, ? 通過Driver 可以獲得數(shù)據(jù)庫連接Connection , ?但是這個(gè)Driver 怎么才能new 出來呢? ?肯定不能直接new , ? ?Java似乎陷入了雞生蛋、蛋生雞的無限循環(huán)中了。
?
最后, 還是Java的反射機(jī)制救了他, 不要直接new 了, ?每個(gè)數(shù)據(jù)庫的Driver 都用一個(gè)文本字符串來表示, 運(yùn)行時(shí)動(dòng)態(tài)的去裝載, 例如mysql 的Driver 是這樣的:
Oracle是這樣的:
只要這個(gè)Driver Class不改動(dòng), 其他具體的實(shí)現(xiàn)像Connection, ?Statement, ResultSet想怎么改就怎么改。
?
接下來的問題是同一個(gè)程序可能訪問不同的數(shù)據(jù)庫, 可能有多個(gè)不同Driver 都被動(dòng)態(tài)裝載進(jìn)來, 如何來管理?
?
那就搞一個(gè)DriverManager吧, ?Mysql 的Driver, Oracle的Driver 在類初始化的時(shí)候, 一定得注冊(cè)到DriverManager中來, 這樣DriverManager才能管理啊:
注意: 關(guān)鍵點(diǎn)是static 的代碼塊, 在一個(gè)類被裝載后就會(huì)執(zhí)行。
?
DriverManager 可以提供一個(gè)getConnection的方法, 用于建立數(shù)據(jù)庫Connection 。
DriverManager會(huì)把這些信息傳遞給每個(gè)Driver , 讓每個(gè)Driver去創(chuàng)建Connection 。
?
慢著! ?如果DriverManager ?里既有MysqlDriver, 又有OracleDriver , ?這里到底該連接哪一個(gè)數(shù)據(jù)庫呢? ? 難道讓兩個(gè)Driver 都嘗試一下? 那樣太費(fèi)勁了吧, 還得區(qū)分開,沒法子只好讓那些程序在數(shù)據(jù)庫連接url中來指定吧:
url中指明了這是一個(gè)什么數(shù)據(jù)庫, 每個(gè)Driver 都需要判斷下是不是屬于自己支持的類型, 是的話就開始連接, 不是的話直接放棄。
?
(劉欣注: 每個(gè)Driver接口的實(shí)現(xiàn)類都需要實(shí)現(xiàn)一個(gè)acceptsURL(Sting url)方法, 判斷這個(gè)url是不是自己能支持的。 )
?
唉,真是不容易啊, Java想, 這下整個(gè)體系就完備了吧, 為了獲得一個(gè)Connection , 綜合起來其實(shí)就這么幾行代碼:
無論是任何數(shù)據(jù)庫, 只要正確實(shí)現(xiàn)了Driver, Connection 等接口, 就可以輕松的納入到JDBC框架下了。
?
Java終于可以高興的宣布: “JDBC正式誕生了!”
?
轉(zhuǎn)自碼農(nóng)翻身微信公眾號(hào)文章
JDBC的誕生
?
總結(jié)
以上是生活随笔為你收集整理的码农翻身——JDBC的诞生的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 码农翻身(随笔)
- 下一篇: 获取Bing主页的背景图片