JDBC连接数据库过程是怎样的,其中JDBC是如何建立数据库连接、建立连接过程中对字符集编码是如何处理的,本文将简要介绍其中流程。
1、通过JDBC连接数据库过程
JDBC是常用的应用连接数据库的方式,以MySQL数据库为例,连接数据库包括以下步骤:
1)加载数据库驱动
将MySQL的JDBC驱动类加载到JVM中,JVM查找并加载指定类初始化执行DriverManager.registerDriver(new Driver()),将驱动实例注册到DriverManager的驱动列表中。
// MySQL 8.0+ 驱动类名
Class.forName("com.mysql.cj.jdbc.Driver");
// 旧版驱动类名
// Class.forName("com.mysql.jdbc.Driver");
2)建立数据库连接
DriverManager会遍历所有注册驱动,每个驱动尝试解析URL(MySQL驱动识别jdbc:mysql:前缀)。驱动与数据库服务器建立TCP连接,验证用户名/密码和权限,返回Connection对象实例,最终创建与数据库的物理连接。
// 获取数据库连接
String url = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC";
String username = "root";
String password = "password";
Connection connection = DriverManager.getConnection(url, username, password);
3)创建statement对象
创建用于执行SQL语句的对象,可以选择普通Statement或PreparedStatement。Statement每次执行都需要重新编译SQL语句,不支持参数化查询;PreparedStatement预编译SQL语句,支持参数化查询,防止SQL注入,提高性能。
// 创建普通Statement
Statement statement = connection.createStatement();
// 创建PreparedStatement (推荐)
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
4)执行SQL语句
通过TCP连接发送SQL到数据库服务器,服务器解析并优化SQL,执行SQL操作(查询/更新),并返回结果。对于查询可以使用executeQuery()执行查询,返回ResultSet结果集指针;对于更新可以使用executeUpdate()执行更新,并返回受影响行数;也可以使用通用的执行execute()来执行SQL语句。
// 查询操作
ResultSet resultSet = statement.executeQuery(
"SELECT * FROM users WHERE age > 18"
);
// 更新操作
int rowsAffected = statement.executeUpdate(
"UPDATE users SET name = '张三' WHERE id = 1"
);
// 执行DDL语句
boolean result = statement.execute(
"CREATE TABLE IF NOT EXISTS products (id INT PRIMARY KEY)"
);
5)处理结果集
以查询为例,从ResultSet中读取数据,通常需要遍历所有行。
while(rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
// 处理数据...
}
ResultSet常用方法有很多,包括next()移动到下一行、getString(columnName)获取字符串类型列值、wasNull()检查上一个值是否为NULL等。
6)释放资源
按照ResultSet → Statement → Connection的顺序关闭资源,释放数据库连接。
finally {
if(rs != null) rs.close(); // 关闭结果集
if(stmt != null) stmt.close(); // 关闭语句
if(conn != null) conn.close(); // 关闭连接
}
每个close()都会向服务器发送关闭请求,连接不关闭会导致资源泄漏和连接耗尽。
数据库连接创建成本高,实际生产环境适应中务必使用连接池如Druid进行访问,可提升应用性能。JDBC连接数据库的流程如下图所示:

2、JDBC建立数据库连接过程

如图所示,JDBC和数据库建立连接过程如下:
1)驱动加载与注册
驱动加载方式有显示的加载,如Class.forName(“com.mysql.cj.jdbc.Driver”),也可以将驱动包如mysql-connector-java.jar放入classpath后,由ServiceLoader自动发现驱动。
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
底层实现上JVM加载驱动类并执行静态初始化块,然后将驱动实例注册到DriverManager的registeredDrivers列表,最后每个驱动类实现java.sql.Driver接口的acceptsURL()方法。
2)连接URL解析
jdbc:mysql://<host>:<port>/<database>?<key1>=<value1>&<key2>=<value2>
URL中的参数包括很多,不同的驱动版本也有不同的参数配置:
- host:数据库服务器IP/域名(默认localhost)
- port:监听端口(默认3306)
- database:目标数据库名
- serverTimezone:时区设置
- characterEncoding:字符编码(建议UTF-8)
- 等等
3)驱动匹配过程
当程序调用DriverManager.getConnection()时,会遍历所有注册的驱动实例,并调用每个驱动的acceptsURL(url)方法。如果是MySQL驱动识别"jdbc:mysql:"前缀返回true,匹配成功的驱动尝试建立连接。
4)TCP连接建立
驱动会根据host/port创建Socket连接,完成TCP三次握手,建立到MySQL服务器的双向通信通道。默认超时时间为30秒,可通过socketTimeout参数调整。
5)MySQL协议认证
服务器发送初始握手包(包括协议版本、服务器版本、线程ID)返回给客户端,客户端响应认证包(用户名、密码、数据库名)。服务器验证账号权限和密码(使用mysql_native_password),成功则发送OK包,失败返回ERR包。
6)连接参数协商
认证成功后进行会话参数设置,包括字符集(characterEncoding)、时区(serverTimezone)、
事务隔离级别等,客户端发送SET命令初始化会话环境。比如设置字符集为utf8mb4:
SET NAMES utf8mb4;
7)返回connection对象
驱动创建com.mysql.cj.jdbc.ConnectionImpl实例,封装底层Socket连接和会话状态,返回给应用程序的JDBC Connection接口对象。
JDBC和数据库建立连接的完整流程如下图所示:

3、JDBC连接数据库时字符集处理流程
JDBC连接数据库过程中,URL连接串中的字符集编码处理逻辑涉及多个关键参数:
- useUnicode=true,启用Unicode支持(必须设为 true,否则 characterEncoding 无效)。
- characterEncoding=UTF-8,指定客户端发送数据到服务器的编码(如 UTF-8、GBK)。
- characterSetResults=UTF-8,控制服务器返回结果的编码(可选,默认同 characterEncoding)
在建立连接的时候,驱动首先以服务器默认编码建立基础连接, 如果URL 中指定了 characterEncoding 且 useUnicode=true,驱动自动向服务器发送命令:
SET NAMES '<characterEncoding>'
连接建立后,客户端Java 字符串 (UTF-16)转码为 characterEncoding 指定编码,然后发送给服务器。服务器返回 character_set_results 编码的数据,驱动按相同编码解码为 UTF-16。
在MySQL 8.0+版本数据库中,如果URL串中指定了字符集为UTF-8,驱动会默认转换为utf8/utf8mb4。JDBC驱动加载字符集编码过程如下:
1)字符集名称解析
当在URL中设置 characterEncoding=UTF-8 时,驱动会进行字符集名称解析
// 伪代码展示核心逻辑
String charsetName = params.get("characterEncoding"); // 获取 "UTF-8"
Charset charset = Charset.forName(charsetName); // 关键调用点
2)JDK字符集加载
JDK 维护着 sun.nio.cs.StandardCharsets 中的预注册字符集:
// JDK 内部注册表 (简化版)
Map<String, Charset> builtinCharsets = Map.of(
"UTF-8", StandardCharsets.UTF_8,
"ISO-8859-1", StandardCharsets.ISO_8859_1,
"US-ASCII", StandardCharsets.US_ASCII
);
如果非内置字符集,通过 SPI 加载:
ServiceLoader<CharsetProvider> loader = ServiceLoader.load(CharsetProvider.class);
for (CharsetProvider provider : loader) {
Charset cs = provider.charsetForName(charsetName);
if (cs != null) return cs;
}
若未找到则抛出 UnsupportedCharsetException,导致 JDBC 连接失败
3)MySQL驱动特殊处理
MySQL 驱动维护特殊映射关系(com.mysql.cj.CharsetMapping):
// 驱动内部的字符集别名映射
Map<String, String> mysqlToJdkMapping = Map.of(
"utf8mb4", "UTF-8", // MySQL别名 → JDK标准名
"latin1", "Cp1252", // Windows兼容
"sjis", "Shift_JIS" // 日语支持
);
也就是在JDK中加载到的字符集会在驱动层进行映射,比如UTF-8在MySQL驱动层映射为utf8mb4,最后在数据库服务端会设置字符集:
set NAMES utf8mb4
处理流程如下图所示:

需要注意的是,JDK版本不同,支持的字符集也不同,可以通过Charset.forName进行验证
Charset charset = Charset.forName(charsetName);
也可以列出JDK支持的所有字符集
Charset.availableCharsets().keySet().forEach(System.out::println);
// 输出 JDK 支持的所有字符集
参考资料:
- http://www.cs.ucf.edu/courses/cnt4714/spr2013/jdbc.ppt
JDBC连接数据库流程及字符集处理详解
1623

被折叠的 条评论
为什么被折叠?



