JDBC 底层原理

概述

JDBC(Java DataBase Connectivity)是Java和数据库之间的一个桥梁,是一个「规范」而不是一个实现,能够执行SQL语句。JDBC由一组用Java语言编写的类和接口组成。各种不同类型的数据库都有相应的实现,注意:本文中的代码都是针对MySQL数据库实现的。

先看一个案例:

```java
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 步骤如下:

  1. 数据库驱动:Class.forName("com.mysql.jdbc.Driver");
  2. 获取链接:Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
  3. 创建Statement或者PreparedStatement对象: Statement stmt = conn.createStatement();
  4. 执行sql数据库查询:ResultSet rs = stmt.executeQuery("SELECT id, name, age FROM m_user where id =1");
  5. 解析结果集:System.out.println("name: "+rs.getString("name")+" :年龄"+rs.getInt("age"));
  6. 最后就是各种资源的关闭。

数据库驱动

安装好数据库之后,应用程序是不能直接使用数据库的,必须要通过相应的数据库驱动程序,通过驱动程序去和数据库打交道。其实也就是数据库厂商的JDBC接口实现,即对Connection等接口的实现类的jar文件。

Driver接口:此接口是提供给数据库厂商实现的。比如说MySQL的,需要依赖对应的jar包

MySQL数据库对应的实现驱动实现类:

```java
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),把程序需要驱动类注册进去。

```java
//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);

深入聊聊这行代码,到底底层是怎么连接数据库的?

方法三个参数:链接地址,用户名和密码。

```java
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()));
}
```

获取连接的关键代码aDriver.driver.connect(url,info); 这个方法是每个数据库驱动自己的实现的。

```java
// 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;
                }
            }
        }
}
```

获取连接的关键代码aDriver.driver.connect(url,info); 这个方法是每个数据库驱动自己的实现的。

```java
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从这个类名应该能猜到还不到真正连接的,只是创建一个连接Url相关信息封装。
        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;
        }
    }
}


public static JdbcConnection getInstance(HostInfo hostInfo) throws SQLException {
        return new ConnectionImpl(hostInfo);
}
```

ConnectionImpl构造方法里有调用createNewIO方法:

```java
@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方法,
        //其中,这里的session是NativeSession
        this.session.connect(this.origHostInfo,this.user,this.password,this.database,DriverManager.getLoginTimeout()*1000,c);
        this.session.setQueryInterceptors(this.queryInterceptors);
 }

 public void connect(HostInfo hi,String user,String password,String database,int loginTimeout,TransactionEventHandler transactionManager)throws IOException{
        SocketConnection socketConnection=new NativeSocketConnection();
        //看到socket连接了,后续就是socket的连接数据库的过程了
        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());
}
```

com.mysql.cj.protocol.a.NativeSocketConnection#connect

```java
@Override
public 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方法:

```java
public 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();
}
```

总结

数据库驱动依赖SPI类加载机制

获取连接是通过socket与数据库取得连接的

文章整理自互联网,只做测试使用。发布者:Lomu,转转请注明出处:https://www.it1024doc.com/5013.html

(0)
LomuLomu
上一篇 2024 年 12 月 31 日
下一篇 2024 年 12 月 31 日

相关推荐

  • Mysql

    MySQL 学习整理 MySQL 基础架构 最上层的客户端所包含的服务并不是 MySQL 独有的,大多数基于网络的客户端/服务器工具或服务器都有类似的服务,包括连接处理、身份验证、确保安全性等。 第二层包含了大多数 MySQL 的核心功能,包括查询解析、分析、优化、以及所有的内置函数(例如,日期、时间、数学和加密函数),所有跨存储引擎的功能也都在这一层实现:…

    2024 年 12 月 31 日
    52500
  • Java技术前沿——大数据机器学习在金融衍生品风险建模中的创新实践(244)

    🌟亲爱的技术探索者们,诚挚欢迎来到【云端科技驿站】!在这个数字化浪潮奔涌的时代,我们共同搭建了一个融合创新与实用的技术交流平台。这里不仅有前沿的技术解析,更期待您分享独到见解,让我们携手在科技海洋中扬帆远航!🌟全网平台(微信公众号/CSDN/抖音/华为/支付宝/微博):云端科技一、加入【技术精英联盟】快速通道1:【云端科技精英社群(2024新版)】快速通道2…

    2025 年 5 月 19 日
    33600
  • 交易系统:退款单模型设计详解

    大家好,我是汤师爷~ 和退款单作为整个交易逆向系统的核心,支撑着售后管理环节。 售后域核心概念模型 1、退款单 退款单是记录和跟踪退款处理过程的核心业务单据,包含以下关键信息: 租户ID:标识所属商户或组织 退款单ID:退款单的唯一标识 原订单ID:关联的原始订单 业务类型:仅退款、退货退款等 退款类型:如全额退款、部分退款、按商品退款等 创建时间:退款单生…

    2024 年 12 月 30 日
    59700
  • 如何用串口调试助手ComTone调试串口?附安装包

    前言 大家好,我是小徐啊。我们在调试应用的时候,有时候是需要进行串口通信的。但并不是每次都有实时的串口数据供我们去测试,这个时候就需要一个模拟生成串口数据的工具来帮助我们了。今天,小徐就来介绍下串口调试助手ComTone的用法。文末附获取方式。 如何使用串口调试助手ComTone 首先,需要选择对应的端口号,这个必须是能联通的串口号,然后点击打开串口按钮,如…

    2025 年 1 月 10 日
    55100
  • 阿里一面:那我把线程池coreSize配置成0会怎样?

    写在前面 设想一下,在我们的项目中存在一个边缘的业务流程,它并不频繁地被触发。在设计线程池时,我回想起了线程池的常见配置原则。为了最大限度地节省资源,我将核心线程数(corePoolSize)设置为0。这样的配置是否能够顺利执行任务呢? 线程池配置原则回顾 在任务提交时,线程池会根据以下策略进行处理: 如果线程池中的线程数少于核心线程数,则创建一个新线程来执…

    2024 年 12 月 26 日
    59400

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信