第5篇-MySQL驱动的JDBC实现
约 1587 字大约 5 分钟
2026-05-24
四、ResultSetImpl:结果集的"迭代器"
4.1 next():移动到下一行
// 类路径:com.mysql.cj.jdbc.ResultSetImpl
@Override
public boolean next() throws SQLException {
// 【核心】检查是否有下一行
if (this.rowData == null) {
return false;
}
// 【核心】移动到下一行
this.rowData.next();
// 【核心】判断是否还有数据
if (this.rowData.isAfterLast()) {
return false;
}
return true;
}白话文解释:
rowData是ResultSetImpl内部用来存储数据的对象。它通常是一个ArrayList,存放了所有的行数据。
ext()方法会把"游标"往下移一行。如果移完还有数据,返回true;否则返回 false`。
4.2 getString():获取列的值
// 类路径:com.mysql.cj.jdbc.ResultSetImpl
@Override
public String getString(String columnLabel) throws SQLException {
// 【核心】根据列名获取列索引
int columnIndex = findColumn(columnLabel);
// 【核心】获取列的值
return getStringInternal(columnIndex);
}
private String getStringInternal(int columnIndex) throws SQLException {
// 【核心】从当前行数据中获取指定列的值
Object value = this.rowData.getCurrentRow()[columnIndex - 1];
if (value == null) {
return null;
}
// 【核心】转换成 String 类型
return value.toString();
}白话文解释:
findColumn(columnLabel)会根据列名(比如 ame),找到对应的列索引(比如1`)。this.rowData.getCurrentRow()返回当前行的数据(一个数组)。- 然后,根据列索引,从数组中取出对应的值。
五、MySQL 协议:JDBC 的"底层语言"
你可能想知道:JDBC 是如何与 MySQL 服务器通信的?
答案是:通过 MySQL 协议。这是一个应用层协议,定义了:
- 如何建立连接(登录验证)。
- 如何发送 SQL(命令包)。
- 如何接收结果(结果包)。
- 如何关闭连接(退出包)。
5.1 登录验证流程
客户端(JDBC) 服务器(MySQL)
| |
|--- 登录请求包(用户名、密码)--->|
| |
|<--- 登录响应包(成功/失败)-----|
| |白话文解释:
- 客户端发送一个"登录请求包",里面包含了用户名、密码、数据库名、字符集等信息。
- 服务器验证用户名和密码。如果正确,返回一个"成功"响应;否则,返回一个"失败"响应(包含错误信息)。
5.2 SQL 执行流程
客户端(JDBC) 服务器(MySQL)
| |
|--- SQL 命令包(SELECT * FROM user)--->|
| |
| |(执行 SQL)
| |
|<--- 结果包(数据行)---------------|
| |白话文解释:
- 客户端发送一个"SQL 命令包",里面包含了要执行的 SQL。
- 服务器执行 SQL,然后把结果(数据行)封装成"结果包",返回给客户端。
- 客户端解析"结果包",封装成
ResultSetImpl对象。
六、埋下伏笔:MyBatis 如何与 MySQL 驱动协作?
说到这里,你可能会问:MyBatis 是如何使用 MySQL 驱动的?它又是如何执行 SQL 的?
这个问题,我们会在系列三(MyBatis 系列)中详细解答。这里先预告一下核心流程:
- MyBatis 通过
SqlSession执行 SQL。 SqlSession内部使用了Executor(执行器)。Executor在执行 SQL 前,会通过SpringManagedTransaction获取数据库连接。SpringManagedTransaction会通过DataSourceUtils.getConnection()获取连接。DataSourceUtils.getConnection()最终调用dataSource.getConnection(),也就是 Druid 的getConnection()方法。- Druid 的
getConnection()最终会调用Driver.connect(),创建 MySQL 连接。 - 获取到连接后,MyBatis 会创建
Statement(或PreparedStatement),并通过它执行 SQL。
下一篇文章的预告:《数据源系列(六):连接的生命周期与事务边界》
七、总结与思考
今天我们一起深入了 MySQL 驱动的 JDBC 实现,了解了 Driver、ConnectionImpl、StatementImpl、ResultSetImpl 的核心实现,以及 MySQL 协议的基本原理。
让我们用几个问题来检验一下今天的学习成果:
MySQL 驱动的入口类是什么?
com.mysql.cj.jdbc.Driver。- 它的静态代码块会把自己注册到
DriverManager。
Driver.connect()方法做了什么?- 解析 URL,提取主机名、端口、数据库名等信息。
- 创建
ConnectionImpl对象。 - 发送登录请求,验证用户名和密码。
Statement.executeQuery()的执行流程是怎样的?- 发送 SQL 数据包给 MySQL 服务器。
- 接收 MySQL 返回的结果包。
- 解析结果包,封装成
ResultSetImpl对象。
MySQL 协议的作用是什么?
- 定义了 JDBC 与 MySQL 服务器通信的格式。
- 包括:登录验证、SQL 执行、结果返回、连接关闭等流程。
八、扩展阅读
【扩展点】自定义 JDBC 驱动
你可以自定义 JDBC 驱动,用于特殊的场景。比如,你可以实现一个" Mock 驱动",用于单元测试:
// 【扩展点】自定义 Mock 驱动示例
public class MockDriver implements Driver {
static {
try {
DriverManager.registerDriver(new MockDriver());
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public Connection connect(String url, Properties info) throws SQLException {
// 【注意】返回一个 Mock 连接(不真正连接数据库)
return new MockConnection();
}
@Override
public boolean acceptsURL(String url) throws SQLException {
// 【注意】只处理以 "jdbc:mock:" 开头的 URL
return url.startsWith("jdbc:mock:");
}
}场景:你在做单元测试时,不希望真正连接数据库(因为太慢,而且需要配置数据库环境)。这时,你可以使用 Mock 驱动,让它返回"假数据"。
如何使用 Mock 驱动? 在测试代码中:
// 【扩展点】使用 Mock 驱动
@Test
public void testMockDriver() throws SQLException {
// 【注意】注册 Mock 驱动
DriverManager.registerDriver(new MockDriver());
// 【注意】获取 Mock 连接
Connection conn = DriverManager.getConnection("jdbc:mock:test");
// 【注意】执行 SQL(不会真正执行,只是返回 Mock 数据)
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM user");
while (rs.next()) {
System.out.println(rs.getString("name"));
}
}好了,今天的文章就到这里。下一篇,我们将深入连接的"生命周期"与"事务边界",看看连接是如何被管理的。敬请期待!
如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、转发!你的支持是我继续创作的动力!
系列文章目录:
- 系列一:Spring 事务系列(已完成)
- 第1篇:@Transactional 注解的"前世今生"
- 第2篇:AOP 代理 —— 事务的"隐身衣"
- 第3篇:事务传播机制源码深度解析
- 第4篇:事务隔离级别的实现原理
- 第5篇:事务管理器的设计哲学
- 第6篇:事务资源的绑定与传递
- 第7篇:提交、回滚与异常规则
- 系列二:数据源系列(进行中)
- 第1篇:JDBC 规范与 DriverManager 的 legacy(已完成)
- 第2篇:Druid 连接池的整体架构(已完成)
- 第3篇:连接的借与还(getConnection 全流程)(已完成)
- 第4篇:Druid 过滤链与监控统计(已完成)
- 第5篇:MySQL 驱动的 JDBC 实现(本文)
- 第6篇:连接的生命周期与事务边界
- 系列三:MyBatis 系列(待开始)
- 系列四:总结系列(待开始)