简单分析JDBC如何获取数据库连接

122 阅读3分钟
故事的开头

我们知道JDBC(Java DataBase Connectivity)就是java数据库连接,由java定义的api来规定java如何来访问关系型数据库的。(小声哔哔:定义规则的才是大佬)

各个不同的数据库都通过实现JDBC的接口从而实现java访问数据库执行SQL语句,实现的jar包就叫做数据库驱动,接下来通过简单的分析一下oracle驱动是如何实现JDBC的。

image-20220330151623120.png

上代码

接下来通过一个程序代码跟进来看看JDBC如何获得数据库连接

package com.demo;
​
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
​
public class JDBCTest {
​
    /**
     * 数据库地址 替换成本地的地址
     */
    private static final String url = "jdbc:oracle:thin:@127.0.0.1:1521/ORCL";
    /**
     * 数据库用户名
     */
    private static final String username = "******";
    /**
     * 密码
     */
    private static final String password = "******";
​
    public static void main(String[] args) {
        try {
            // 1. 加载数据库驱动
            Class.forName("oracle.jdbc.OracleDriver");
            // 2. 获得连接
            Connection connection = DriverManager.getConnection(url, username, password);
            // 3. 创建sql语句
            String sql = "select * from t_test_user";
            Statement statement = connection.createStatement();
            // 4. 执行sql
            ResultSet result = statement.executeQuery(sql);
            // 5. 处理结果
            while(result.next()){
                System.out.println(result.getString(3));
                System.out.println("result = " + result.getString(1));
            }
            // 6. 关闭连接
            result.close();
            connection.close();
        } catch (Exception e){
            System.out.println(e);
        }
​
    }
}

上面的代码中四个引用分别是

  • DriverManager 驱动管理这个类里有一个数据结构用来存储Driver的实现。
  • Connection 代表一次数据库连接通过DriverManager.getConnection(String url, String user, String password)获得。
  • Statement 主要用来执行SQL语句,通过connection.createStatement()获得
  • ResultSet结果集主要用来记录SQL执行后的返回值,通过statement.executeQuery(String sql)获得

首先第一行代码

// 1. 加载数据库驱动
            Class.forName("oracle.jdbc.OracleDriver");

加载数据驱动,将oracle.jdbc.OracleDriver类加载到Java虚拟机对应代码如下:

image-20220330160620484.png

继续跟进oracle.jdbc.driver.OracleDriver类

image-20220330160728750.png

可以看到该类继承自java的java.sql.Driver默认加载进行一坨的初始化其中有个静态块初始化很重要如下:

static {
        try {
            // 默认初始化defaultDriver就是null进入if条件判断
            if (defaultDriver == null) {
                // 初始化defaultDriver
                defaultDriver = new oracle.jdbc.OracleDriver();
                // 调用java驱动管理的注册驱动方法
                DriverManager.registerDriver(defaultDriver);
            }
​
            ...不要在意以下细节我也没看懂...
    }

继续跟进来到DriverManager中的registerDriver方法:

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 */
        // 翻译翻译什么叫他*的叫惊喜,翻译翻译
        /* 如果list中没有这个驱动就把这个驱动注册到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);
​
    }

查看registeredDrivers在DriverManager中的成员变量

// 用来存储驱动对象
    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();

开个坑啊CopyOnWriteArrayList类有时间看一下!!!

看到这下面用大腿肚子也能想到DriverManager.getConnection(String url, String user, String password)应该就是从registeredDrivers中拿出实际的驱动Driver来创建Connection代码如下:

private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
        ...省略...
        // 循环registeredDrivers获得实际驱动对象
        for(DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    // 通过oracle驱动对象获取Connect
                    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;
                    }
                }
​
            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }
​
        }
​
        ...省略...
    }
总结

为什么这么实现?

首先第一点面向接口编程,我们知道关系型数据库茫茫多,每个数据库的底层实现都不尽相同,连接方式也都各有不同。如果每个数据库由java实现一套连接方案这样的程序毫无扩展性,新出一个数据库我们就要开发一套代码这是我们这种蠢人才会干的,大佬写的代码最让人舒服和值得学习的地方就是可扩展性,而且每个数据库都有自己的知识产权,你想要全部交由你处理人家还不一定同意。所以解决方案变成了由java来制定接口,每个数据库进行各自封装实现,最后统一为连接,执行,返回结果。

其次Class.forName("oracle.jdbc.OracleDriver");这个设计真的是让我拍案叫绝,按照我的传统思维要加载oracle的driver一定就是

Driver driver = new OracleDriver();

如果这么写不免落了下乘,这样程序与Oracle驱动包高度耦合,不利于扩展而当前的情况下我们可以通过配置文件来进行切换数据库,不用每次进行代码修改非常值得学习。

\