你不要过来啊:洞察 Java JDBC 流程核心源码的实现逻辑

575 阅读3分钟

Java JDBC(Java Database Connectivity)是一个用于执行SQL语句的Java API,它能够与各种关系数据库进行交互。JDBC的核心流程主要包括以下几个步骤:

  1. 加载驱动程序
  2. 建立连接
  3. 创建和执行SQL语句
  4. 处理结果集
  5. 关闭资源

以下是这些步骤在JDBC中的实现细节及其源码分析。

1. 加载驱动程序

在使用JDBC时,首先需要加载数据库驱动程序。这通常通过Class.forName()方法来完成,该方法会注册相应的驱动程序。

try {
    Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

2. 建立连接

通过DriverManager.getConnection()方法建立数据库连接。

String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "root";
String password = "password";

Connection conn = null;
try {
    conn = DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
    e.printStackTrace();
}

DriverManager 源码分析

DriverManager类负责管理一组JDBC驱动程序。当调用getConnection()时,它会遍历已注册的驱动程序列表,找到合适的驱动并返回连接。

public class DriverManager {
    private static final CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();

    public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException {
        if (driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver));
        } else {
            throw new NullPointerException();
        }
    }

    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);
    }

    public static Connection getConnection(String url, java.util.Properties info) throws SQLException {
        for (DriverInfo aDriver : registeredDrivers) {
            try {
                Connection result = aDriver.driver.connect(url, info);
                if (result != null) {
                    return result;
                }
            } catch (SQLException e) {
                // 忽略并尝试下一个驱动
            }
        }
        throw new SQLException("No suitable driver found for " + url);
    }
}

3. 创建和执行SQL语句

通过Connection对象创建StatementPreparedStatement对象,并通过它们执行SQL查询或更新。

Statement stmt = null;
ResultSet rs = null;

try {
    stmt = conn.createStatement();
    rs = stmt.executeQuery("SELECT * FROM mytable");

    while (rs.next()) {
        System.out.println(rs.getString("column1"));
    }
} catch (SQLException e) {
    e.printStackTrace();
}

Statement 源码分析

Statement接口用于执行静态SQL语句并返回其生成的结果。

public interface Statement extends Wrapper, AutoCloseable {
    ResultSet executeQuery(String sql) throws SQLException;
    int executeUpdate(String sql) throws SQLException;
    void close() throws SQLException;
}

public class StatementImpl implements Statement {
    private ConnectionImpl connection;

    public StatementImpl(ConnectionImpl connection) {
        this.connection = connection;
    }

    public ResultSet executeQuery(String sql) throws SQLException {
        // 执行查询并返回结果集
        return new ResultSetImpl();
    }

    public int executeUpdate(String sql) throws SQLException {
        // 执行更新并返回受影响的行数
        return 1;
    }

    public void close() throws SQLException {
        // 关闭语句
    }
}

4. 处理结果集

通过ResultSet对象获取查询结果。

try {
    while (rs.next()) {
        int id = rs.getInt("id");
        String name = rs.getString("name");
        System.out.println("ID: " + id + ", Name: " + name);
    }
} catch (SQLException e) {
    e.printStackTrace();
}

ResultSet 源码分析

ResultSet接口表示SQL查询的结果集。

ResultSet 接口

public interface ResultSet extends Wrapper, AutoCloseable {
    boolean next() throws SQLException;
    String getString(int columnIndex) throws SQLException;
    String getString(String columnLabel) throws SQLException;
    int getInt(int columnIndex) throws SQLException;
    int getInt(String columnLabel) throws SQLException;
    void close() throws SQLException;
    // ... 还有许多其他方法,例如 getDouble, getDate 等
}

简单的 ResultSet 实现类

为了简单起见,这个实现类是非常简化的,仅供理解流程之用。

public class ResultSetImpl implements ResultSet {
    private List<Map<String, Object>> data;
    private int cursor;

    public ResultSetImpl(List<Map<String, Object>> data) {
        this.data = data;
        this.cursor = -1; // 初始化指针在第一条记录之前
    }

    @Override
    public boolean next() throws SQLException {
        cursor++;
        return cursor < data.size();
    }

    @Override
    public String getString(int columnIndex) throws SQLException {
        Map<String, Object> row = data.get(cursor);
        return (String) row.values().toArray()[columnIndex - 1];
    }

    @Override
    public String getString(String columnLabel) throws SQLException {
        Map<String, Object> row = data.get(cursor);
        return (String) row.get(columnLabel);
    }

    @Override
    public int getInt(int columnIndex) throws SQLException {
        Map<String, Object> row = data.get(cursor);
        return (int) row.values().toArray()[columnIndex - 1];
    }

    @Override
    public int getInt(String columnLabel) throws SQLException {
        Map<String, Object> row = data.get(cursor);
        return (int) row.get(columnLabel);
    }

    @Override
    public void close() throws SQLException {
        // 关闭结果集
    }
}

5. 关闭资源

在完成数据库操作后,应该关闭所有打开的资源,例如ResultSetStatementConnection

finally {
    try {
        if (rs != null) rs.close();
        if (stmt != null) stmt.close();
        if (conn != null) conn.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

完整的 JDBC 示例流程

结合以上所有步骤,以下是一个完整的JDBC示例:

import java.sql.*;

public class JDBCDemo {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;

        try {
            // 1. 加载驱动程序
            Class.forName("com.mysql.cj.jdbc.Driver");

            // 2. 建立连接
            String url = "jdbc:mysql://localhost:3306/mydatabase";
            String user = "root";
            String password = "password";
            conn = DriverManager.getConnection(url, user, password);

            // 3. 创建语句对象并执行查询
            stmt = conn.createStatement();
            rs = stmt.executeQuery("SELECT * FROM mytable");

            // 4. 处理结果集
            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                System.out.println("ID: " + id + ", Name: " + name);
            }
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            // 5. 关闭资源
            try {
                if (rs != null) rs.close();
                if (stmt != null) stmt.close();
                if (conn != null) conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

这个示例展示了如何按照JDBC的核心流程进行数据库操作,从加载驱动程序、建立连接,到创建和执行SQL语句以及处理结果集,最后关闭所有资源。实际应用中,常常会使用更高级的机制来管理这些资源,例如使用连接池或框架(如Spring)的JDBC模板。