国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

沒想到你是這樣的JDBC

開發 開發工具
本文將介紹 MySQL Client 與 Server 的通信原理,以及 Java JDBC 的工作原理等。什么是JDBC 的 Type4,什么又是 Type 3?

本文將介紹 MySQL Client 與 Server 的通信原理,以及 Java JDBC 的工作原理等。什么是JDBC 的 Type4,什么又是 Type 3? 

一、 MySQL Client & Server

我們在進行數據庫的操作時,總是通過 GUI 數據管理工具,或者命令行連接到 MySQL 的 Server 上,然后進行一系列數據庫的創建、表與表內數據的操作等。

這個時候,這一系列 GUI管理工具,或者命令行,都是一個 MySQL 的 Client, 然后將 Client 的一系列操作命令,發送給 Server。 這里在發送時,Client 的命令都是根據 MySQL 規范,生成的一個個packet進行發送。

更直觀的理解, MySQL 的 Client 和 Server 相當于是 Socket 通信中的一個 Client 與 Server, 彼此按照約定的協議格式進行通信。

二、 JDBC 是什么?

什么是 JDBC 呢? 你一定會脫口而出,不就是通過它連庫嘛。 這么理解只是其中的一小部分,「灑灑水的啦」。

JDBC 全稱:The Java Database Connectivity,要從兩個方面來理解。

  • API
  • Driver

API , 首先是一個標準,并不針對特定的數據庫,做為一個高層抽象,提供Java 語言與眾多數據庫之間的連通。 通過JDBC API,我們不再需要根據不同的數據庫使用不同的操作方式,而是以一種標準的操作,實現『Write Once, Run anywhere』。

既然 API 是個標準,就需要有相對應的實現, 這里的 Driver 就是各個數據庫廠商根據標準進行的針對實現。這也是為什么在應用開發時,連MySQL 使用 MySQL 的 connector,連接 Oracle 使用 Oracle 的驅動的原因。

畢竟如何和自己廠家的數據庫交互,只有各個廠商自己清楚,所以根據標準,各個廠商開發自己的 Connector。

下圖來自官方文檔,來描述 JDBC 的作用以及請求中所處的位置。

 JDBC 的作用以及請求中所處的位置

圖的左側,也稱為Type4, 是通過Driver 直接連接數據庫 Server。這種也是最常用的,通過Driver ,將JDBC 的請求轉成數據庫服務器可以識別的協議格式。

圖的右側, 稱為Type 3 是通過Driver,將JDBC 的請求轉成 中間件的協議格式。

以MySQL為例,看到這里我們發現,其實 JDBC 的操作,本質上相當于是一個 MySQL 的 Client,通過 Driver,把應用里的查詢、刪除等操作「翻譯」成了 MySQL Server 可識別的協議格式,再傳遞過去執行。

所以,整個JDBC 做的事情可以歸結為以下三件:

  1. 創建數據庫連接
  2. 發送 SQL statement
  3. 處理請求結果

JDBC 總結起來的兩個部分,數據庫服務提供方,開發XXXDriver, 應用開發者使用Driver 連接數據庫,進行數據庫操作。

這樣應用開發者就不需要關心底層與數據庫交互時的協議實現,如何進行請求連接,交互等,可以更專心到自己的業務。 否則,每個開發者都需要處理一次和數據交互的協議,繁瑣而且不易,重復勞動。

三、MySQL connector-J 部分源碼

有了上述的「理論」知識后,我們來看點干的。 MySQL 的驅動包是開源的,我們可以很方便的進行下載了解實現。

最傳統的 JDBC 使用,一般都是通過以下這種方式:

  • Connection c = DriverManager.getConnection(url, user,pwd);
  • Statement stmt = c.createStatment
  • stmt.executeQuery 拿結果

getConnection的時候一般都需要提供一個URL,這個URL也都是固定寫法,比如mysql的是 jdbc:mysql://,這一部分是按照規范,同時在Driver的代碼里,通過解析URL獲取要連接到的主機,端口,以及其他的連接參數。

  1. public Properties parseURL(String url, Properties defaults) throws java.sql.SQLException { 
  2.         Properties urlProps = (defaults != null) ? new Properties(defaults) : new Properties(); 
  3.         if (url == null) { 
  4.             return null; 
  5.         } 
  6.         if (!StringUtils.startsWithIgnoreCase(url, URL_PREFIX) && !StringUtils.startsWithIgnoreCase(url, MXJ_URL_PREFIX) 
  7.                 && !StringUtils.startsWithIgnoreCase(url, LOADBALANCE_URL_PREFIX) && !StringUtils.startsWithIgnoreCase(url, REPLICATION_URL_PREFIX)) { 
  8.             return null; 
  9.         } 
  10.         int beginningOfSlashes = url.indexOf("//"); 
  11.         if (StringUtils.startsWithIgnoreCase(url, MXJ_URL_PREFIX)) { 
  12.             urlProps.setProperty("socketFactory", "com.mysql.management.driverlaunched.ServerLauncherSocketFactory"); 
  13.         } 

看這一部分源碼可以發現,除了我們常用的url配置,還可以在其中進行loadbalance的配置等等。長了見識。

  1. DriverManager.getConnection(xx,xx,xx) 這個方法最終會調用 Service Provider 已經加載的 Driver中可用的driver,調用driver的getConnection方法,對應到Mysql的源碼,就是下方這個,重點是`com.mysql.jdbc.ConnectionImpl.getInstance` 
  2.  
  3. public java.sql.Connection connect(String url, Properties info) { 
  4.         if (url == null) { 
  5.             throw SQLError.createSQLException(Messages.getString("NonRegisteringDriver.1"), SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, null); 
  6.         } 
  7.         if (StringUtils.startsWithIgnoreCase(url, LOADBALANCE_URL_PREFIX)) { 
  8.             return connectLoadBalanced(url, info); 
  9.         } else if (StringUtils.startsWithIgnoreCase(url, REPLICATION_URL_PREFIX)) { 
  10.             return connectReplicationConnection(url, info); 
  11.         } 
  12.         Properties props = null
  13.         if ((props = parseURL(url, info)) == null) { 
  14.             return null; 
  15.         } 
  16.         if (!"1".equals(props.getProperty(NUM_HOSTS_PROPERTY_KEY))) { 
  17.             return connectFailover(url, info); 
  18.         } 
  19.         try { 
  20.             Connection newConn = com.mysql.jdbc.ConnectionImpl.getInstance(host(props), port(props), props, database(props), url); 
  21.             return newConn; 

再來看 getInstance具體做了啥?

  1. protected static Connection getInstance(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url) 
  2.             throws SQLException { 
  3.         if (!Util.isJdbc4()) { 
  4.             return new ConnectionImpl(hostToConnectTo, portToConnectTo, info, databaseToConnectTo, url); 
  5.         } 
  6.         return (Connection) Util.handleNewInstance(JDBC_4_CONNECTION_CTOR, 
  7.                 new Object[] { hostToConnectTo, Integer.valueOf(portToConnectTo), info, databaseToConnectTo, url }, null); 
  8.     } 
  1. this.io = new MysqlIO(newHost, newPort, mergedProps, getSocketFactoryClassName(), getProxy(), getSocketTimeout(), 
  2.                 this.largeRowSizeThreshold.getValueAsInt()); 
  3. this.io.doHandshake(this.user, this.password, this.database); 

我們看,先通過MysqlIO創建了一個IO連接,然后進行握手

  1. // save last exception to propagate to caller if connection fails 
  2.                 SocketException lastException = null
  3.                 // Need to loop through all possible addresses. Name lookup may return multiple addresses including IPv4 and IPv6 addresses. Some versions of 
  4.                 // MySQL don't listen on the IPv6 address so we try all addresses. 
  5.                 for (int i = 0; i < possibleAddresses.length; i++) { 
  6.                     try { 
  7.                         this.rawSocket = createSocket(props); // 這里創建了一個空的Socket對象 
  8.                         configureSocket(this.rawSocket, props); //將一些超時之類的屬性設置到socket中 
  9.                         InetSocketAddress sockAddr = new InetSocketAddress(possibleAddresses[i], this.port); //獲取host對應的ip地址等,再加上端口,組成一個Address 
  10.                         // bind to the local port if not using the ephemeral port 
  11.                         if (localSockAddr != null) { 
  12.                             this.rawSocket.bind(localSockAddr); 
  13.                         } 
  14.                         this.rawSocket.connect(sockAddr, getRealTimeout(connectTimeout)); //實際連接到服務器 

連接Mysql的url中,可以分成好幾類,例如可以連接到mysql進行loadbalanner, jdbc:mysql:loadbalancer//xxx 還有進行replicated

我們在使用JDBC連接時,一定會常使用PreparedStatement, 這個稱為預編譯sql,其中可以設置一些占位符

那這些占位符是啥時候填充進去的呢?

查看Mysql Connector 的源碼,我們發現,實際前面的createPreparedStatment,setXX之類的時候,

只是設置到對應的變量里記錄了下來,

在執行executeQuery的時候,會再從前面記錄下來的變理中提取出來,做為值填充到原來的sql占位中去

整個sql做為一個packet發送過去。

這個時候也就更容易理解為啥預編譯不容易被SQL 注入,而拼接SQL容易。 因為預編譯在替換占位符時,即使你的值里有類似于 「--」 這一類的危險內容,或者 1==1, 都是做為一個column的value 來使用,而拼接SQL,則會放到完整的語句中,在執行時被全部解析,導致問題。

以下就是 MySQL Connector 在執行 sql 時的調用棧。

 

  1. java.lang.Thread.State: RUNNABLE 
  2.   at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3633) 
  3.   at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2460) 
  4.   at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2625) 
  5.   at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2551) 
  6.   - locked <0x5a3> (a com.mysql.jdbc.JDBC4Connection) 
  7.   at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1861) 
  8.   at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:1962) 

 

整個背后其實原理也和我們前面說的一樣,比較簡單,是通過一個TCP Socket 方式,在獲取到OutputStream,接裝好的SQL,

在執行的時候,是寫到這個Output里,發送到 Mysql的服務器。

返回值是怎么獲取的呢? 是將返回的Buffer轉換成ResultSet

 

  1. ResultSetInternalMethods rs = readAllResults(callingStatement, maxRows, resultSetType, resultSetConcurrency, streamResults, catalog, resultPacket, 
  2.                   false, -1L, cachedMetadata); 

 

此外,在實際的業務開發中,對于在代碼中拿到的一個Connection,可能會遇到網絡抖動,數據庫服務異常等情況。有連接問題之前,我們可以先檢測連接是否可用,來避免繼續使用有問題的Connection,導致問題一直存在。

檢測一個連接是否可用,可以通過執行一條最簡單的 `select 1` 來判斷是否有異常,當然,在JDBC的標準里,也包含一個檢查連接是否可用的方法 isValid

實現原理,對于MySQL 的Connctor-J客戶端,是通過向Server發送一條ping的命令,來檢測連接的狀態。

總結一下,我們通過幾個部分來介紹了 MySQL Client 與 Server 的交互原理,以及JDBC 是什么,是通過什么方式來和 Server 進行交互的。

順道再分享下最近遇到的一個和數據庫連接有關的小插曲。在處理一個問題,增加數據庫連接檢查之后,功能正確就上線了。上線不久,接到另一個服務提供方報警,說我們發送了其不能處理的數據庫指令。 黑人問號臉。我只是通過獲取數據庫狀態的一個getAttribute的方式來檢查下連接啊。 據說他們收到的是show xxx status之類的指令。 那為啥不能識別呢?

仔細問了一下,是由于他們提供的特殊 Proxy 服務,只實現了MySQL 的部分指令解析,所以對應show xxx 不支持,而我們項目里默認以為全部的client 都支持全集指令,導致問題。之后改了一個檢查方式解決了報警問題。

所以,在開發時,也需要再考慮下接入的服務,是否會按照規范,把全部內容實現了。

【本文為51CTO專欄作者“侯樹成”的原創稿件,轉載請通過作者微信公眾號『Tomcat那些事兒』獲取授權】

戳這里,看該作者更多好文

責任編輯:趙寧寧 來源: 51CTO專欄
相關推薦

2019-03-08 10:08:41

網絡程序猿代碼

2018-05-02 09:38:02

程序員代碼互聯網

2023-02-26 00:00:02

字符串分割String

2019-08-19 09:21:36

程序員Bug代碼

2018-06-27 14:23:38

機器學習人工智能入門方法

2021-01-27 18:13:35

日志nginx信息

2016-03-04 14:14:02

電話免費越洋

2017-12-26 15:41:26

2018-12-26 09:44:02

分布式緩存本地緩存

2024-01-04 12:33:17

ChatGPTAI視頻

2022-03-21 08:55:53

RocketMQ客戶端過濾機制

2017-02-09 17:00:00

iOSSwiftKVC

2012-12-28 13:47:36

Raspberry PGeek

2022-01-05 17:13:28

監控HTTPS網站

2021-11-29 05:37:24

Windows Def操作系統微軟

2020-08-14 08:19:25

Shell命令行數據

2009-04-28 07:48:29

蓋茨打工基金會

2018-07-10 09:07:57

AI數據科技

2022-11-02 07:46:31

GoFrameGcache緩存

2018-10-22 15:29:50

點贊
收藏

51CTO技術棧公眾號

麻豆传媒一区二区| 一区在线不卡| 精品国产免费一区二区三区四区| 福利精品一区| 岛国视频一区| 国产清纯美女被跳蛋高潮一区二区久久w | 超污网站在线观看| 91精品国产麻豆| jizz18欧美18| 欧美午夜精品久久久久久蜜| 国产精品美女一区二区三区| 在线三级中文| 国产精品成人播放| 国产成人精品一区二区三区四区| 色视频在线看| 欧美激情免费看| 久久精品国产精品青草| 亚洲成人男人天堂| 麻豆精品一区二区综合av| 日本熟妇人妻xxxxx| 欧美人动与zoxxxx乱| 网红女主播少妇精品视频| 青青草影院在线观看| 在线观看av一区二区| 试看120秒一区二区三区| 亚洲成人自拍| 在线影院国内精品| 少妇精品久久久| 熟妇人妻va精品中文字幕| 日韩女优电影在线观看| 国产精品成人a在线观看| 日韩不卡一二三| 在线电影av不卡网址| 日韩精品每日更新| 久久久久久女乱国产| 欧美最猛性xxxxx(亚洲精品)| 成人av网站大全| av电影在线观看| 国产国产精品人在线视| 99国内精品久久| 4438x成人网全国最大| 91麻豆国产精品| 一区二区三区日韩欧美| 91蝌蚪精品视频| 欧美日韩第二页| 久久久国产精品x99av| 国产成人亚洲综合色影视| sm在线播放| 亚洲成人精品电影在线观看| 欧美另类极品| 国产精品污网站| 日韩美女av在线| 国产精品日韩久久久| 黄色直播在线| 奇米4444一区二区三区| 国产欧美一区二区精品秋霞影院| 日韩欧乱色一区二区三区在线 | 久久一区二区三区喷水| 国产一级电影网| 8050国产精品久久久久久| 中文无字幕一区二区三区| 视频在线亚洲| 国产主播中文字幕| 久久久久久久久国产精品| 中文子幕无线码一区tr| 国产精品久久久久av蜜臀| 国产三级三级看三级| 91精品国产91久久久久久不卡| 国产精品对白交换视频| 日本国产精品| 8×8x拔擦拔擦在线视频网站| 国产精品jvid在线观看蜜臀| 亚洲无人区一区| 88国产精品视频一区二区三区| 亚洲一区在线日韩在线深爱| 亚洲最大的免费| 4438亚洲最大| 麻豆精品视频在线观看免费| jk漫画禁漫成人入口| 亚洲爆乳无码精品aaa片蜜桃| 毛片av免费在线观看| 91在线看www| 国产精品久久久久久久久| 亚洲二区视频| 黄色小网站在线观看| 日本精品视频一区| 亚洲精品中文字幕av| 成人a区在线观看| 久久亚洲资源中文字| 午夜免费高清视频| 国产三级精品网站| 日韩免费性生活视频播放| 国产精品自拍av| 91精品啪在线观看国产爱臀| 亚洲小说区图片区情欲小说| 免费看成人片| 精品国产一区av| 欧美日韩国产一中文字不卡| 日韩福利电影在线| 国产一区二区主播在线| 日本加勒比高清在线| 国产一区二区三区高清视频| 一区二区三区久久精品| 亚洲美女一区二区三区| 亚洲精华国产欧美| 国产精品传媒麻豆hd| 男女视频网站免费观看| 欧美在线播放一区二区| 欧美精品激情在线观看| 欧美日韩国产综合草草| 337p粉嫩大胆色噜噜噜噜亚洲| 希岛爱理一区二区三区| 亚洲日本欧美在线| 成人免费看片'免费看| 国产精品毛片a∨一区二区三区|国 | 中文字幕国产亚洲2019| 欧美大成色www永久网站婷| 2014国产精品| 无码人妻精品一区二区三区在线| 最近中文字幕在线中文视频| 综合久久2019| 老司机成人在线| 日韩精品一级中文字幕精品视频免费观看| a美女胸又www黄视频久久| 欧美在线色视频| 九九热这里只有精品6| 欧美大香线蕉线伊人久久| 色综合小说天天综合网| 动漫一区二区| 国产一级一区二区| 亚洲一级在线观看| 日韩有码视频在线| 免费观看黄色大片| 黄色av免费在线观看| 精品一区三区| 久久综合中文字幕| 日韩视频一区二区三区在线播放| 国产精品va在线播放我和闺蜜| www.在线观看av| 91桃色在线观看| 99久久综合精品| 亚洲福利视频三区| 国产成+人+综合+亚洲欧洲 | 久久香蕉国产线看观看av| 亚洲成人蜜桃| 99久久精品免费看国产小宝寻花| 亚洲精品网址| 色呦呦国产精品| 久久涩涩网站| 91精品久久| 蜜臀av一区二区在线免费观看 | 亚洲精品免费一二三区| 91精品国产91久久久久久| 看av免费毛片手机播放| 日韩高清在线观看一区二区| av网站免费线看精品| 欧美老妇交乱视频| 午夜激情福利在线| 日韩精品导航| 色视频一区二区| 日韩精品不卡| 国产精品迅雷| 欧美激情一区二区三区四区| 欧美性受xxx| 久热国产在线| 狠狠色丁香久久婷婷综合_中| 精品日韩一区二区三区 | 欧美成人精品影院| 亚洲精品少妇久久久久久| 888久久久| 国产亚洲精品久久久| 成人漫画网站免费| 欧美日韩三级| 少妇高潮 亚洲精品| 国产香蕉尹人视频在线| 老司机一区二区三区| www.国产一区| 免费在线毛片| 成人免费毛片aaaaa**| 国色天香2019中文字幕在线观看| 大乳在线免费观看| thepron国产精品| 精品视频第一区| 久久伊人久久| 欧美图片一区二区三区| 亚洲精品乱码久久久久久自慰| 亚洲第一偷拍| 51精品国自产在线| 日韩视频亚洲视频| 日韩视频中午一区| 国产又粗又长又爽视频| 视频欧美一区| 欧美一卡2卡三卡4卡5免费| 欧美一级黄色录像片| 日韩精品免费一区二区三区| 日韩精品中文字幕一区| 国产毛片视频| 成人短视频下载| 97人人香蕉| 国产精品色呦|