面試被問:JDBC底層是如何連接資料庫的?

2021-01-07 田維常

背景

前兩天一個小夥伴面試的時候,被問JDBC底層是如何連接資料庫的?

他頓時一臉懵逼,因為大部分人只知道JDBC的幾個步驟,至於底層到底是怎麼連接資料庫的,還真不知道。

由於小夥伴是面試高級開發,問這種問題倒也不能說面試官過分,如果是初級或者中級,那問著問題就確實有些過分了。

但是如果你在初級或者中級的階段,就知道了答案,豈不是爽歪歪麼?

估計大部分人都不知道這個問題該怎麼回答,稍微發散一下思維,倒是可以猜測一下,今天我們就來搞清楚JDBC底層到底是如何連接資料庫的。往後別再猜了。

反過來,如果面試官問你JDBC的時候,你能知道底層是怎麼連接資料庫的,估計,很多相對較水的面試官也會一臉懵逼。

何為 JDBC ?

JDBC(Java DataBase Connectivity)是Java和資料庫之間的一個橋梁,是一個「規範」而不是一個實現,能夠執行SQL語句。JDBC由一組用Java語言編寫的類和接口組成。各種不同類型的資料庫都有相應的實現,注意:本文中的代碼都是針對MySQL資料庫實現的。

JDBC 架構

分為雙層架構和三層架構。

雙層

作用:此架構中,Java Applet 或應用直接訪問數據源。

條件:要求 Driver 能與訪問的資料庫交互。

機制:用戶命令傳給資料庫或其他數據源,隨之結果被返回。

部署:數據源可以在另一臺機器上,用戶通過網絡連接,稱為 C/S配置(可以是內聯網或網際網路)。

三層

側架構特殊之處在於,引入中間層服務。

流程:命令和結構都會經過該層。

吸引:可以增加企業數據的訪問控制,以及多種類型的更新;另外,也可簡化應用的部署,並在多數情況下有性能優勢。

歷史趨勢:以往,因性能問題,中間層都用 C 或 C++ 編寫,隨著優化編譯器(將 Java 字節碼 轉為 高效的 特定機器碼)和技術的發展,如EJB,Java 開始用於中間層的開發這也讓 Java 的優勢突顯出現出來,使用 Java 作為伺服器代碼語言,JDBC隨之被重視。

入門案例

下面給出一個

JDBC

入門級案例:

public class JdbcDemo {public static final String URL = "jdbc:mysql://localhost:3306/mblog"; public static final String USER = "root"; public static final String PASSWORD = "123456"; public static void main(String[] args) throws Exception { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT id, name, age FROM m_user where id =1"); while(rs.next()){ System.out.println("name: "+rs.getString("name")+" 年齡:"+rs.getInt("age")); } }}

JDBC 步驟

資料庫驅動:

Class.forName("com.mysql.jdbc.Driver");

獲取連接:

Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);

創建

Statement

或者

PreparedStatement

對象:

Statement stmt = conn.createStatement();

執行sql資料庫查詢:

ResultSet rs = stmt.executeQuery("SELECT id, name, age FROM m_user where id =1");

解析結果集:

System.out.println("name: "+rs.getString("name")+" 年齡:"+rs.getInt("age"));

最後就是各種資源的關閉。

資料庫驅動

加載MySql的驅動類 :

Class.forName("com.mysql.jdbc.Driver");

我們安裝好資料庫之後,我們的應用程式也是不能直接使用資料庫的,必須要通過相應的資料庫驅動程序,通過驅動程序去和資料庫打交道。其實也就是資料庫廠商的JDBC接口實現,即對Connection等接口的實現類的jar文件。

Driver接口

java.sql.Driver

此接口是提供給資料庫廠商實現的。比如說MySQL的,需要依賴對應的jar包。

<dependency><groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.16</version></dependency>

MySQL資料庫對應的實現驅動實現類:

package com.mysql.cj.jdbc;import java.sql.SQLException; public class Driver extends NonRegisteringDriver implements java.sql.Driver {static { try { //註冊驅動 java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } public Driver() throws SQLException { }}

DriverManager是rt.jar包下的類,(rt=runtime),把我們需要驅動類註冊進去。

//DriverManager類中的方法public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da)throws SQLException { /* Register the driver if it has not already been added to our list */ if(driver != null) { registeredDrivers.addIfAbsent(new DriverInfo(driver, da)); } else { // This is for compatibility with the original DriverManager throw new NullPointerException(); } println("registerDriver: " + driver);}

相應裝載Oracle驅動:

Class.forName("oracle.jdbc.driver.OracleDriver");

Sql Server驅動:

Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");

獲取連結

給我們看起來就這一行代碼:

Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);

下面我們進行深入聊聊這行代碼,到底底層是怎麼連接資料庫的?

getConnection

方法三個參數:連結地址,用戶名和密碼。

public static Connection getConnection(String url,String user, String password) throws SQLException { java.util.Properties info = new java.util.Properties(); if (user != null) { info.put("user", user); } if (password != null) { info.put("password", password); } return (getConnection(url, info, Reflection.getCallerClass())); }

創建一個Properties對象,Properties是

HashTable

的子類。

public class Properties extends Hashtable<Object,Object> {//.....}

再看

getConnection

方法:

// Worker method called by the public getConnection() methods.private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException { ClassLoader callerCL = caller != null ? caller.getClassLoader() : null; SQLException reason = null; //遍歷氣門註冊的資料庫驅動 for(DriverInfo aDriver : registeredDrivers) { try { //獲取連接 Connection con = aDriver.driver.connect(url, info); if (con != null) { // Success! println("getConnection returning " + aDriver.driver.getClass().getName()); return (con); } } catch (SQLException ex) { if (reason == null) { reason = ex; } } }}

這段代碼的關鍵是這一句代碼:

Connection con = aDriver.driver.connect(url, info);

connet()

方法是每個資料庫驅動自己的實現的。

package com.mysql.cj.jdbc;public class NonRegisteringDriver implements java.sql.Driver {@Override public java.sql.Connection connect(String url, Properties info) throws SQLException { //部分無關鍵要的代碼省略 //下面是重點 ConnectionUrl conStr = ConnectionUrl.getConnectionUrlInstance(url, info); switch (conStr.getType()) { //SINGLE_CONNECTION("jdbc:mysql:", HostsCardinality.SINGLE), // case SINGLE_CONNECTION: return com.mysql.cj.jdbc.ConnectionImpl.getInstance(conStr.getMainHost()); case LOADBALANCE_CONNECTION: return LoadBalancedConnectionProxy.createProxyInstance((LoadbalanceConnectionUrl) conStr); case FAILOVER_CONNECTION: return FailoverConnectionProxy.createProxyInstance(conStr); case REPLICATION_CONNECTION: return ReplicationConnectionProxy.createProxyInstance((ReplicationConnectionUrl) conStr); default: return null; } }}

ConnectionUrl從這個類名應該能猜到還不到真正連接的,只是創建一個連接Url相關信息封裝。

public abstract class ConnectionUrl implements DatabaseUrlContainer {private static final String DEFAULT_HOST = "localhost"; private static final int DEFAULT_PORT = 3306; //...}

熟悉的身影,MySQL資料庫默認埠。我們繼續看下一行重要的代碼:

ConnectionImpl.getInstance(conStr.getMainHost());

這裡就是獲取一個實例,不出意外,連接就在這裡面產生的。繼續:

//ConnectionImplpublic static JdbcConnection getInstance(HostInfo hostInfo) throws SQLException {return new ConnectionImpl(hostInfo);}

ConnectionImpl構造方法裡有調用createNewIO方法:

@Override public void createNewIO(boolean isForReconnect) { synchronized (getConnectionMutex()) { try { if (!this.autoReconnect.getValue()) { connectOneTryOnly(isForReconnect); return; } connectWithRetries(isForReconnect); } catch (SQLException ex) { } } }private void connectOneTryOnly(boolean isForReconnect) throws SQLException { Exception connectionNotEstablishedBecause = null; JdbcConnection c = getProxy(); //又看到熟悉的connet方法, this.session.connect(this.origHostInfo, this.user, this.password, this.database, DriverManager.getLoginTimeout() * 1000, c); this.session.setQueryInterceptors(this.queryInterceptors); }

其中,這裡的session是NativeSession。

public void connect(HostInfo hi, String user, String password, String database, int loginTimeout, TransactionEventHandler transactionManager)throws IOException { SocketConnection socketConnection = new NativeSocketConnection(); socketConnection.connect(this.hostInfo.getHost(), this.hostInfo.getPort(), this.propertySet, getExceptionInterceptor(), this.log, loginTimeout); this.protocol.connect(user, password, database); this.protocol.getServerSession().setErrorMessageEncoding(this.protocol.getAuthenticationProvider().getEncodingForHandshake()); }

在這個方法裡,我們看到了Socket的命名開頭的類,哈哈,是不是就是使用Socket進行通信的呢?

精彩繼續:

socketConnection.connect(this.hostInfo.getHost(), this.hostInfo.getPort(), ...);

來到NativeSocketConnection類中方法:

//com.mysql.cj.protocol.a.NativeSocketConnection@Overridepublic void connect(String hostName, int portNumber, PropertySet propSet, ExceptionInterceptor excInterceptor, Log log, int loginTimeout) { this.mysqlSocket = this.socketFactory.connect(this.host, this.port, propSet, loginTimeout); //... }

這裡的socketFactory是StandardSocketFactory。所以也就是調用的是StandardSocketFactory的connect方法:

//StandardSocketFactorypublic <T extends Closeable> T connect(String hostname, int portNumber, PropertySet pset, int loginTimeout) throws IOException {this.rawSocket = createSocket(pset); this.rawSocket.connect(sockAddr, getRealTimeout(connectTimeout));} protected Socket createSocket(PropertySet props) { return new Socket();}

這裡就算到底了,說白

JDBC

的底層就是使用「Socket」進行連接資料庫的。

常用方法

獲取Statement

三種類型

要執行SQL語句,必須獲得java.sql.Statement實例,Statement實例分為以下3 種類型:

執行靜態SQL語句。通常通過Statement實例實現。執行動態SQL語句。通常通過PreparedStatement實例實現。執行資料庫存儲過程。通常通過CallableStatement實例實現。具體獲取方式

Statement stmt = con.createStatement() ; PreparedStatement pstmt = con.prepareStatement(sql) ; CallableStatement cstmt = con.prepareCall("{CALL demoSp(? , ?)}") ;

常用方法

Statement和PreparedStatement的異同及優缺點

同:兩者都是用來執SQL語句的

異:PreparedStatement需要根據SQL語句來創建,它能夠通過設置參數,指定相應的值,不是像Statement那樣使用字符串拼接的方式。

PreparedStatement的優點:

1、其使用參數設置,可讀性好,不易記錯。在statement中使用字符串拼接,可讀性和維護性比較差。

2、其具有預編譯機制,性能比statement更快。

3、其能夠有效防止SQL注入攻擊。

execute和executeUpdate的區別

相同點:二者都能夠執行增加、刪除、修改等操作。

不同點:

1、execute可以執行查詢語句,然後通過getResult把結果取出來。executeUpdate不能執行查詢語句。

2、execute返回Boolean類型,true表示執行的是查詢語句,false表示執行的insert、delete、update等。executeUpdate的返回值是int,表示有多少條數據受到了影響。

相關焦點

  • 面試被問:JDBC底層是如何連接資料庫的?|sql|mysql|數據源|java|...
    背景  前兩天一個小夥伴面試的時候,被問JDBC底層是如何連接資料庫的?  他頓時一臉懵逼,因為大部分人只知道JDBC的幾個步驟,至於底層到底是怎麼連接資料庫的,還真不知道。  由於小夥伴是面試高級開發,問這種問題倒也不能說面試官過分,如果是初級或者中級,那問著問題就確實有些過分了。
  • JDBC這個問題,問的小夥伴一臉懵逼
    背景前兩天一個小夥伴面試的時候,被問JDBC底層是如何連接資料庫的?他頓時一臉懵逼,因為大部分人只知道JDBC的幾個步驟,至於底層到底是怎麼連接資料庫的,還真不知道。由於小夥伴是面試高級開發,問這種問題倒也不能說面試官過分,如果是初級或者中級,那問著問題就確實有些過分了。但是如果你在初級或者中級的階段,就知道了答案,豈不是爽歪歪麼?
  • 如何正確地創建和銷毀軟體應用系統中JDBC資料庫連接對象實例
    軟體項目實訓及課程設計指導——如何正確地創建和銷毀軟體應用系統中JDBC資料庫連接對象實例1、Java語言中提供有訪問資料庫系統的JDBC編程接口Java 資料庫連接技術(JDBC,Java DataBase Connectivity)其實是將Java語言與標準的
  • 什麼是JDBC?Java資料庫連接性簡介
    本文提供了JDBC的概述,然後是使用JDBC API將Java客戶端與輕量級關係資料庫SQLite連接的動手入門。JDBC如何工作JDBC作為基於C的ODBC(開放資料庫連接)API的替代產品而開發,提供了編程級別的接口,該接口處理Java應用程式與資料庫或RDBMS通信的機制。
  • java中資料庫:JDBC的使用
    通過JDBC,我們可以用java編寫程序,實現與特定的資料庫進行連接,向資料庫發送SQL語句,實現對資料庫的特定操作,並對資料庫返回的結果進行處理。JDBC編程步驟:步驟一:根據應用程式所用的資料庫,選擇JDBC驅動程序類型;步驟二:連接到資料庫,得到Connection對象;步驟三:通過Connection創建Statement對象;步驟四:使用Statement對象提交SQL語句;步驟五:操作結果集;步驟六:回收資料庫資源。
  • 「詳細」MySQL資料庫與JDBC編程
    Class.forName("com.mysql.ch.hdbc.Driver");Class.forName("oracle.jdbc.driver.OracleDriver");2、通過DriverManager獲取資料庫連接DriverManager.getConnection("jdbc:mysql
  • Spring JDBC訪問關係型資料庫
    點擊上面「天碼營」,加入我們,快速成長~「內容簡介」資料庫操作是我們搭建應用的基本操作,今天我們來學習一下如何利用
  • Java使用jdbc連接SQLServer資料庫完成多條件模糊查詢
    引言最近想在網上找些使用jdbc連接操作SQLserver的完成模糊查詢或者多條件Java代碼,
  • 如何應用策略設計模式分離JDBC資料庫連接中的外部環境信息
    (2)策略設計模式的應用場景示例一比如,在構建軟體應用系統持久層資料庫連接Connection接口的對象實例時不希望出現下面的狀況:將實現各種不同物理資料庫系統的「連接」的功能代碼(在這裡代表策略模式中的「算法」)直接出現在使用資料庫連接對象實例的「程序」中(也就是策略模式中的「客戶
  • JDBC資料庫連接
    JDBC全稱為:Java Data Base Connectivity,java資料庫連接。是一種用於執行SQL語句的javaAPI,為多種關係資料庫提供統一訪問。簡單點來說:某個行業都會有一個標準,都會提供一個行業標準文檔,而JDBC就是我們這個行業其中一個資料庫連接標準。它是由一組java語言編寫的類和接口組成。
  • java連接mysql資料庫的五種方式
    Jdbc-----資料庫的五種連接方式第一種方式:將用戶名和密碼封裝在Properties類中首先,導入資料庫連接包這個是毋庸置疑的。創建一個jdbc驅動dirver。將資料庫(以MySQL為例)的url保存在所創建的字符串url中。
  • 如何使用 Spring 對資料庫進行 CURD?
    作者 | 阿文,責編 | 郭芮頭圖 | CSDN 下載自視覺中國作為一名程式設計師,CURD(增刪改查)是一件必不可少的事情,甭管你是初級程式設計師還是高級程式設計師都會和資料庫打交道。那麼在Java中如何通過Spring 框架來對資料庫進行操作呢?本文將帶你一起來學習。
  • 常用資料庫 JDBC URL格式(總結)
    Microsoft SQL ServerMicrosoft SQL Server JDBC Driver (一般用來連接 SQLServer 2000)驅動程序包名:msbase.jar mssqlserver.jar msutil.jar驅動程序類名:com.microsoft.jdbc.sqlserver.SQLServerDriverJDBC
  • 面試官經常喜歡問的Mybatis經典面試題,值得好好收藏哦!
    今天給大家分享一些面試官喜歡提問的Mybatis面試題,好了,廢話不多說,直接上乾貨吧!一、請說說在Mybatis 中#和$有什麼區別?#相當於對數據 加上 雙引號,$相當於直接顯示數據1.$方式一般用於傳入資料庫對象,例如傳入表名.6.一般能用#的就別用$.二、說說在使用JDBC 編程時,它有哪些不足之處,MyBatis 是如何解決這些問題的?1.
  • JDBC原理分析(基本案例和面試知識點詳解,一般人都能看懂)
    JDBC全稱又叫做Java DataBase Connectivity,就是Java資料庫連接,說白了就是用Java語言來操作資料庫。這篇文章主要是對JDBC的原理進行講解。不會專注於其使用。主要是理解其思想並對一些常見的面試題進行講解。
  • 利用PowerDesigner連接Mysql資料庫並逆向所有表關係圖兩種方式
    兩種方式:一種是連接mysql資料庫,另一種是有sql腳本文件的。一:配置PowerDesigner連接到mysql資料庫(使用的是JDBC方式)。1.1:新建文件,選擇mysql.因為我們連接的是mysql。1.2:配置資料庫連接上一步點擊OK之後,在導航欄中Database-->connect... 快捷鍵:ctrl+shift+n。如下圖:在打開的窗口中現在 Connection profile: 然後選擇Configure...
  • JDBC MySQL
    Java 資料庫連接,(Java Database Connectivity,簡稱JDBC)是Java語言中用來規範客戶端程序如何來訪問資料庫的應用程式接口,提供了諸如查詢和更新資料庫中數據的方法。
  • JDBC 回顧
    ,提到數據訪問,學習 Java 的同學瞬間能就想起 JDBC 技術,JDBC 是 Java Database Connectivity 的全稱,是 Java 語言中用來規範客戶端程序如何來訪問資料庫的應用程式接口,提供了諸如查詢和更新資料庫中數據的一套標準的 API,這套標準不同的資料庫廠家之間共同準守,並提供各自的具體實現。
  • 如何正確地應用JDBC API設計實現應用系統持久層中的各個DAO組件
    軟體項目實訓及課程設計指導——如何正確地應用JDBC API設計和實現應用系統持久層中的各個DAO組件1、Java 系統平臺中實現資料庫連接技術相關的APIJava 資料庫連接技術(JDBC,Java DataBase Connectivity)是一種將Java
  • python是如何來連接mysql的
    就像Java連MySQL使用jdbc驅動器一樣,python的標準數據接口Python DB-API為開發人員提供了資料庫應用編程接口,可以支持很多種資料庫,今天我們以mysql為例來介紹python如何操作資料庫。