☰
JDBC连接MySQL实战:从驱动配置到故障排查的完整指南
2026/9/28 5:28:27 网站建设 项目流程

你是不是也遇到过这样的情况:项目里明明配好了 MySQL,代码一跑,控制台直接甩给你一串红色堆栈,DriverManager.getConnection抛异常,ClassNotFoundException 或者 Communications link failure 轮着来。尤其是刚接触 JDBC 的同学,环境装好了却卡在第一步,连个数据库都连不上。这篇就专门解决这个问题,我把从驱动选择、连接参数到代码封装、常见坑排查的完整链路整理了一遍,全是实操,直接照着做就行。

这篇教程适合三类人:一是刚开始学 JavaWeb、正在用 JDBC 写增删改查的初学者,二是马上要面试、想把底层原理和常见报错讲清楚的同学,三是写老项目维护、被各种数据库连接问题折磨的开发者。JDBC 本身不复杂,但细节极多,版本、时区、SSL、驱动包、资源关闭,任何一个环节出问题都够你折腾半小时。本文用 MySQL 8 + JDK 8/17 + IDEA 这套组合,带你从零跑通连接,再逐步深入到连接池和常见故障排查,建议跟着敲一遍,别只看不练。

1. 项目定位与整体思路

先把这个教程的边界划清楚。JDBC 是 Java 访问关系型数据库的官方标准接口,它本身不是实现,而是一套规范,由各数据库厂商提供具体驱动实现。MySQL 官方驱动的类名是com.mysql.cj.jdbc.Driver,不同版本类的路径还不一样,这往往是新手第一个翻车点。要学会 JDBC,目标不是背 API,而是要建立三条主线:获取连接、执行SQL、处理结果。所有框架(MyBatis、Hibernate)底层都是这三件事。

选 JDBC 作为数据库访问方式而不是直接上 MyBatis,原因在于 JDBC 能让你把每一步看得清清楚楚。MyBatis 帮你把结果集映射成了对象,你反而不知道 ResultSet 到底是什么;事务没提交、连接没释放这种问题,用框架出了事更难看。跑通原生 JDBC 之后再上框架,你才能明白框架到底解决了什么问题,出了问题也知道往哪儿查。

本教程的前半部分覆盖原生 JDBC 六大步骤:加载驱动、建立连接、创建语句、执行、处理结果、关闭资源。后半部分是我在实际开发中都会用到的进阶内容:连接信息外置、连接池选型、远程表数据同步。看完之后你应该能独立写一个连接 MySQL 的工具类,并且遇到报错能自己定位是驱动问题、语法问题还是网络问题。

2. 环境准备与驱动选型

2.1 基础环境与版本适配

先看常见组合及坑点。我用的是 JDK 8 和 JDK 17 分别测过,MySQL 8.0.33 和 MySQL 8.0.40,IDEA Community 版。JDK 8 是大多数企业老项目的标配,驱动建议用 8.0.33;JDK 17 配 8.0.33 以上也没问题,但注意 JDK 17 默认强模块化,某些老驱动反射调用会报警告,用新版驱动能避免。

MySQL 版本对连接有决定性影响。MySQL 5.7 默认认证插件是mysql_native_password,而 MySQL 8.0 默认是caching_sha2_password,如果你手上的驱动低于 5.1.40,连接时会直接报Unable to load authentication plugin 'caching_sha2_password'。这就是为什么我强烈建议不要用网上随便下载的 5.x 老驱动去连 MySQL 8。同理,如果你公司还在用 MySQL 5.6,你拿一个 MySQL 8 驱动去连,虽然基本能连上,但部分特性不兼容。

版本选型表我直接给出来:

组件推荐版本说明
MySQL Server8.0+8.0 为长期支持版,认证插件注意适配
JDK8 或 178 适合老项目,17 是新项目首选
mysql-connector-j8.0.33+建议 8.0.33 以上,包名统一为 mysql-connector-j
IDEACommunity 2023+社区版够用,数据库面板问题见第 5 节
Maven3.6+非必须,不强求

装 MySQL 的细节网上多,但我要提两个常被忽视的点:一是安装时字符集选 utf8mb4,否则中文乱码问题后期一定找上门;二是安装路径不要带空格和中文,否则某些驱动读取配置文件会出问题。如果公司有现成环境,你只需要知道账号密码即可,不必非装独立实例。

2.2 驱动包获取与工程集成

驱动获取有两种方式:Maven 坐标和手动下载 jar。我用 Maven 最多,坐标如下:

<dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.0.33</version> </dependency>

注意坐标的变化,我之前写过一篇文章,评论区有人拿 mysql-connector-java 8.0.28 的坐标,也能用,但从 8.0.31 开始官方把 artifact 改名成了 mysql-connector-j,老坐标虽然能解析,但新项目建议直接用新坐标。驱动包下载失败的问题我放在第 5 节专门讲。

如果你不用 Maven,那就手动下载 jar 后放到项目的lib目录,IDEA 里右键 Add as Library。这个操作做完相当于把 jar 加入了 classpath。很多同学卡在这一步:下载了 jar 放进文件夹,却没有 Add as Library,代码编译不过去,驱动加载报 ClassNotFoundException,这个问题根因就是 classpath 里没有这个 jar。

3. 核心代码实现与参数解读

3.1 标准六步连接流程

光说不练假把式,先直接看一段能跑的完整代码。这个类我做了简化,只演示连接和查询,真实项目里肯定要拆工具类,但先看这个你能理解每一步在干嘛:

import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; public class JdbcDemo { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"; String user = "root"; String password = "123456"; try (Connection conn = DriverManager.getConnection(url, user, password); PreparedStatement ps = conn.prepareStatement("SELECT id, name, age FROM user WHERE id = ?")) { ps.setInt(1, 1); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { System.out.println(rs.getInt("id") + " - " + rs.getString("name")); } } } catch (Exception e) { e.printStackTrace(); } } }

这段代码里有几个核心点:

加载驱动这行被我注释掉了,原因后面说。DriverManager.getConnection是真正建立物理连接的地方,底层会做 TCP 三次握手、MySQL 协议握手、认证。?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8是连接参数,每一项都有讲究,3.2 节拆开讲。

PreparedStatement是预编译语句对象,相对于Statement,它能防 SQL 注入,还能避免每次拼接 SQL 字符串的引号地狱。这里我用?占位,然后setInt(1, 1)给第一个占位符赋值。如果值来自用户输入,这一步就是安全界限。

资源关闭用 try-with-resources,这就是 JDK 7 的语法糖。Connection、PreparedStatement、ResultSet都实现了AutoCloseable,这个写法会在 try 块结束时自动关闭它们。顺序上 ResultSet 最先关、其次是 PreparedStatement、最后是 Connection,但 try-with-resources 声明顺序已经决定了关闭顺序的逆序,你按 Connection、PreparedStatement、ResultSet 的顺序声明的,那关闭顺序就是 ResultSet、PreparedStatement、Connection,恰好对。

3.2 连接URL与properties参数拆解

连接 URL 是 JDBC 最容易出错的字符串,没有之一。格式固定为:jdbc:mysql://主机:端口/数据库名?参数1=值1&参数2=值2。端口默认 3306,但公司数据库可能改了端口或者走了跳板机,这里别写死。

我见过很多人连接不上,是因为把localhost当成了万能地址。开发环境没问题,但如果你连的是远程库,必须要用真实的 IP 或域名,而且远程库一般还要在 MySQL 侧开权限、防火墙放行端口,这个在第 5 节展开。

参数拆开讲:

  • useSSL=false:MySQL 8 驱动默认会尝试 SSL 加密连接。本地开发没有配置 SSL 证书链,不显式关掉就容易报Communications link failure。生产环境建议反过来开启并配置证书,但本地上手阶段一律 false。
  • serverTimezone=Asia/Shanghai:驱动和服务器时区不一致会导致日期读取偏移 8 小时甚至 14 小时,这就是所谓的时区报错The server time zone value '�й���ʱ��' is unrecognized。加了它之后时间转换就按 Asia/Shanghai 来。
  • characterEncoding=utf8:明确字符编码,防止从数据库读出的中文乱码。注意,MySQL 8 里 utf8 是 utf8mb3 的别名,真正的 emoji 要用utf8mb4,如果你的库表字符集是 utf8mb4,建议这里写utf8mb4,而不是utf8。很多人的 emoji 存库后变问号,就是这里只写了 utf8。
  • allowPublicKeyRetrieval=true:当用户使用 caching_sha2_password 认证且连接走 SSL 失败时,需要允许客户端获取公钥用于传输密码。本地测试经常要加这个,不加报Public Key Retrieval is not allowed。
  • rewriteBatchedStatements=true:批量插入时性能提升明显,第 3.4 节讲批处理时用到。

推荐配置给一个通用模板:

jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true

3.3 从Statement到PreparedStatement:SQL注入与SQL拼接实战

很多教材至今还在用 Statement,市区演示拼接 SQL。我明确说一句,任何新代码都不应该用 Statement 拼接用户输入。为什么?看这段反面教材:

String name = "admin' OR '1'='1"; String sql = "SELECT * FROM user WHERE name = '" + name + "'";

拼接后变成SELECT * FROM user WHERE name = 'admin' OR '1'='1',这个查询永远返回全部数据。如果后面的逻辑是DELETE FROM user WHERE name = 'admin' OR '1'='1',后果就是全表删除。SQL 注入的本质就是不可信输入进入了 SQL 结构而非数据。

PreparedStatement 的作用是让 SQL 骨架预先编译,参数通过占位符?传入,驱动层面做了转义与类型处理,从根本上杜绝了注入。而且预编译语句在 MySQL 8 的 prepared statement 缓存里有性能优势,循环执行同一条 SQL 时比 Statement 快很多。

所以正确姿势是:凡是 SQL 里需要带入变量,一律用?占位。JDBC 没有理由再用手动拼接。

3.4 结果集处理:查询、批处理、事务与存储过程调用

查询结果通常用 ResultSet 处理。rs.next()是移动游标到下一行,返回 false 表示到底。取值用getInt、getString、getDate等,参数可以用列索引,也可以用列名。列名直观,推荐列名。

批处理是提升大批量写入效率的核心手段。不加批处理,插入 10000 条记录要循环 10000 次网络往返;加了批处理后,驱动攒一批再发,数据库端一次处理。代码套路是这样的:

conn.setAutoCommit(false); String sql = "INSERT INTO user(name, age) VALUES(?, ?)"; try (PreparedStatement ps = conn.prepareStatement(sql)) { for (int i = 0; i < 10000; i++) { ps.setString(1, "user_" + i); ps.setInt(2, i); ps.addBatch(); if (i % 500 == 0) { ps.executeBatch(); } } ps.executeBatch(); conn.commit(); }

这里有两个重点:一是conn.setAutoCommit(false)关闭自动提交,最后统一commit(),这样事务的一致性不会被破坏;二是addBatch之后executeBatch是按组刷,不是攒到最后一次性刷,避免内存膨胀。实测开启rewriteBatchedStatements=true后,性能差距可以达到一个数量级,这个参数在 URL 里已经加了。

事务控制还有一点必须注意:当你关闭了自动提交,如果执行过程中抛了异常,一定要rollback()回滚,否则可能留下半截数据。而且事务结束之后,需要把setAutoCommit(true)恢复,避免连接归还连接池后状态污染。

存储过程调用用 CallableStatement。假设 MySQL 里建好了一个存储过程add_user(Name, Age),JDBC 调用方式如下:

String sql = "{call add_user(?, ?)}"; try (CallableStatement cs = conn.prepareCall(sql)) { cs.setString(1, "张三"); cs.setInt(2, 20); cs.execute(); }

注意 CallableStatement 的 SQL 格式是{call 存储过程名(?, ?)},这是 JDBC 规范规定的转义语法,不能用普通 SQL 直写。存储过程在生产中一般用于报表或者批量归档等复杂逻辑,但维护成本偏高,业务代码里尽量少碰。

4. 实战进阶:配置外置与连接工具类封装

4.1 为什么不硬编码:连接信息外置与资源关闭

第 3.1 节把 URL、用户名、密码写死在代码里,这是演示需求。线上项目如果把密码硬编码,代码仓库一旦泄露,数据库就是裸奔。正确做法是放到配置文件,然后用 Java 的Properties读取。

我只写一个最简单的实现:

创建jdbc.properties文件放在src/main/resources下:

jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4&allowPublicKeyRetrieval=true jdbc.username=root jdbc.password=123456

然后用下面的工具类读取:

import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.util.Properties; public class DbUtil { private static String url; private static String username; private static String password; static { try (InputStream in = DbUtil.class.getClassLoader().getResourceAsStream("jdbc.properties")) { Properties props = new Properties(); props.load(in); url = props.getProperty("jdbc.url"); username = props.getProperty("jdbc.username"); password = props.getProperty("jdbc.password"); Class.forName(props.getProperty("jdbc.driver")); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws Exception { return DriverManager.getConnection(url, username, password); } }

这里静态代码块只执行一次,加载驱动注册到 DriverManager 一次就够了。Class.forName("com.mysql.cj.jdbc.Driver")的作用是触发 Driver 类的静态初始化,向 DriverManager 注册驱动实例。从 JDBC 4.0 开始,驱动 jar 的META-INF/services/java.sql.Driver文件里声明了驱动类,DriverManager 会自动加载,所以即使不写Class.forName也能连上。我保留这行是为了兼容老场景,并让初学者知道驱动加载的旧机制。

配置文件用getResourceAsStream读取是 classpath 方式,打 jar 包后也能正常加载。注意,不要把配置文件放到普通包目录下而用 FileInputStream 读,那样打成 jar 后文件路径就失效了。

4.2 连接池:从单连接走向并发稳定

如果每次查询都新建物理连接,连上、鉴权、断开,整个链路耗时几十毫秒,高并发下系统马上被拖垮。所以生产环境必须用连接池。连接池的原理很像数据库连接的缓存池:预先建好一批连接,用户要连接时从池里拿,用完归还,而不是真的关闭。

目前主流选择是 HikariCP,Spring Boot 2.x 之后默认连接池就是它。它快的原因之一是字节码极致精简、无反射调用。不用自己去实现池,那是重复造轮子,直接用 HikariCP。

Maven 坐标:

<dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>5.0.1</version> </dependency>

最小配置示例:

HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=Asia/Shanghai"); config.setUsername("root"); config.setPassword("123456"); config.setMaximumPoolSize(10); config.setMinimumIdle(5); config.setConnectionTimeout(30000); HikariDataSource dataSource = new HikariDataSource(config); Connection conn = dataSource.getConnection();

参数意义:minimumIdle是池中最少保持的空闲连接,maximumPoolSize是最大连接数,connectionTimeout是拿连接的超时时间。一个典型误区是连接池开得越大越好,实际上单机 MySQL 默认最大连接数是 151,连接数开太大反而会拖垮数据库。一般来说 20 个连接就能支撑几百并发,具体要靠压测调优。

连接池用起来和 DriverManager 很像,getConnection方法签名一样,但语义不同:它是从池中借一个连接,用完close()后连接不是销毁,而是归还池中复用。这就是为什么第 3.1 节强调要用 try-with-resources,如果借了连接不还,池很快耗尽。

5. 常见问题排查与避坑实录

这部分是我从初学者提问和实际项目里收集的高频坑,整理成表再逐个展开。

错误现象直接原因解决方案
ClassNotFoundException: com.mysql.cj.jdbc.Driverjar 不在 classpath确认驱动包已下载且 Add as Library
Access denied for user 'root'@'localhost'账号密码错或主机限制核对密码,远程库授权%访问
Communications link failure网络不同 / 端口不通 / SSL 参数异常ping 通 IP,telnet 端口,加 useSSL=false
Public Key Retrieval is not allowed认证插件兼容问题加 allowPublicKeyRetrieval=true
The server time zone value ... unrecognized时区参数未指定加 serverTimezone=Asia/Shanghai
Unknown database 'test_db'库名不存在或大小写写错确认数据库名与权限
download from maven failedMaven 仓库被墙或镜像未配置换阿里云镜像,详见 5.3
中文乱码、emoji 变问号字符集不统一URL 用 utf8mb4,数据库建库也统一

5.1 驱动加载与连接失败

ClassNotFoundException是最低级的坑,但却是出现频率最高的。核心原因就是驱动 jar 没有进入编译期 classpath。如果你用的 IDE,最简单的一个验证方式:在项目里敲Class.forName("com.mysql.cj.jdbc.Driver"),编译通过了,说明 classpath 没问题。在External Libraries里能看到 mysql-connector-j 的节点才算是真正引入了。

Communications link failure就要复杂一些。先确认 MySQL 服务是否启动:Windows 下net start | findstr mysql,Linux 下systemctl status mysql。再确认端口:telnet 127.0.0.1 3306。如果端口都通,检查 MySQL 是否绑定了地址:bind-address如果是127.0.0.1,那就只允许本机连接,远程连不上,需要改成0.0.0.0。改完记得重启 mysqld。

5.2 MySQL 8 认证插件与SSL错误

MySQL 8 默认认证插件是caching_sha2_password,你说它安全不?安全。但老驱动只认识mysql_native_password。当你遇到Access denied for user但又确信密码没错时,很可能是驱动版本和插件不匹配。解决方式建议优先升级驱动到 8.x,而不是把 MySQL 的认证插件改回旧的。当然,老项目如果 MySQL 5.7 没法动,你就只能新建用户时指定mysql_native_password,办法很多,关键是能定位到根因。

关于 SSL 错误,我实际遇到过的典型是:URL 没有显式写useSSL=false,服务器 SSL 配置又不完整,驱动默认协商加密时失败。处理很简单:本地测试写 false,生产环境不要关,要按 MySQL 官方文档把服务端证书、客户端 truststore 配好,这不是一笔带过的事,需要单独文章。

5.3 Maven 构建报错与 IDEA 社区版数据库面板

搜索热词里出现频率极高的是idea sqlserver jdbc 自动下载 download from maven failed。这个问题的本质不是 IDEA 坏了,而是 Maven 中央仓库在你的网络环境下访问不稳定。换国内镜像就能解决。在 Maven 的settings.xml里加入阿里云镜像:

<mirrors> <mirror> <id>aliyunmaven</id> <mirrorOf>central</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror> </mirrors>

之后在 IDEA 里刷新 Maven 项目,就能看到下载正常。这个操作我每次新换电脑都会做,属于标配环境配置。

关于社区版连不了数据库的问题,我明确回答你:IDEA 社区版没有内置 Database 面板,但连接数据库这件事本身和 IDE 无关,你写 Java 代码照样连。如果你只是想在 IDEA 里看数据库表,有两个办法:一是装 Database Navigator 插件,二是我更推荐的做法——直接用 Navicat 或者 MySQL Workbench,术业有专攻,管理工具和专业工具比 IDE 内置面板好用得多。不要因为 IDE 社区版没有这个功能就怀疑自己环境不对。

5.4 MySQL 启动失败与 socket 连接错误

热搜里有个很典型的错误:

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock'

这个不是 JDBC 错误,是你在命令行用mysql客户端连本地服务时出的。出错原因一般是 mysqld 进程没启动,或者 socket 文件路径不对。解决办法:Linux 上先systemctl start mysql或service mysql start启动服务,再mysql -uroot -p连接。如果服务没启动,JDBC 那边当然也连不上,表现是Communications link failure。

有一回我帮同事排查这个问题,折腾了半小时,最后发现他连的是另一台机器的 MySQL socket 路径。记住:socket 文件只存在于本机 MySQL 数据目录,如果你连的库在 Docker 容器里,容器路径和宿主机路径也不一样。

5.5 中文乱码与日期偏移

如果是插入中文后查出来是问号,先看连接 URL 有没有characterEncoding=utf8mb4,再看 MySQL 配置文件my.cnf里字符集:

[client] default-character-set = utf8mb4 [mysqld] character-set-server = utf8mb4 collation-server = utf8mb4_unicode_ci

改完重启 MySQL。如果数据库是建好的,还要确认表结构字符集已经是 utf8mb4,可用SHOW CREATE TABLE user;检查。如果建表时用了 latin1,连接参数再对也没用,需要转换表字符集。

日期偏移问题我踩过一次。开发机在北京,线上数据库在新加坡机房,服务器时区是+08:00但连接串没写serverTimezone,查询出来的时间比实际少 8 小时。这是一个非常隐蔽的 bug,因为数据显示是对的,直到比较时间时才露馅。解决就是 URL 里加serverTimezone=Asia/Shanghai,如果服务部署在海外,按业务时区来配,不要随意用 UTC。

6. 场景实战:远程表同步到本地的 JDBC 实现思路

搜索词里有一条很常见:「把远程库的这张表同步到本地。 提供详细操作步骤。」能搜到这个问题,说明你大概率在开发环境想要一份生产数据的副本来做调试复现。用 JDBC 做这个事比导出 SQL 再导入更灵活,适合表结构稳定、需要定期同步的场景。

设计思路是:建立两条连接,一条指向远程数据库,一条指向本地,查询远程表数据,然后批量插入本地。关键点:不要在插入时一条一条执行,否则几万条数据插入会非常慢,等半天还报超时。要开启批处理、关闭自动提交。

数据库相关参数写死在代码里,我按长期工具来写。核心思路分三步:

第一步,建立远程和本地两条连接。远程 URL 把localhost换掉即可,注意远程库如果开了 TLS,参数要相应调整。

第二步,查询远程表用SELECT,固定列顺序。为保证数据不丢失,建议先DELETE FROM 本地表再全量插入,但这会清掉本地表里本来有的数据,有没有影响你自己判断。如果你的同步目标是追加模式,就别删,只插入新增行,可以用主键查重:

SELECT id, name, age FROM remote_user WHERE id NOT IN (SELECT id FROM local_user)

这个 SQL 在数据量小的时候能用,几十万行的全表 NOT IN 性能很差。更好的做法是定时记录一个上次同步的最大主键,下次只拉id > 上次最大id的数据。

第三步,批量插入本地库并提交。这个代码我贴前面批处理小节已经给过套路,不再重复,只强调一点:一批别攒太多,500 条一批实测最稳定。

这个工具扩展空间很大,你可以把它改成定时任务,用 ScheduledExecutorService 每天凌晨跑一次;也可以把目标端从 MySQL 换成 TDengine,思路完全一样,查 MySQL 然后换驱动写入 TDengine 的超表和子表,这是另一个话题了。

7. 实操心得与最后提醒

文章写到这里,JDBC 的核心链路已经走了一遍:环境准备、驱动获取、连接 URL、PreparedStatement、事务、连接池、常见坑排查、远程同步。我最后再分享三点基于个人项目的体会。

第一,不要跳过原生 JDBC 直接学框架。这句话看起来反主流,但我的真实感受是,JDBC 就像数据库访问的「汇编语言」,你理解了底层,MyBatis 的#{}和${}的区别才真正记得住,连接池参数调优才不再靠猜。我给新人的建议是写一个不依赖任何框架的增删改查小项目,把 Connection 的打开和关闭逼着自己手写 50 遍以上,肌肉记忆就形成了。

第二,排查问题要从链路的一端开始。数据库连不上的时候,我习惯按顺序排查:MySQL 服务活没活、端口通不通、账号密码对不对、驱动 classpath 有没有、URL 参数是否冲突。很多人一上来就看代码,然后怀疑人生,其实八成是环境问题。JDBC 代码就那么几行,错的概率远小于环境。

第三,连接和资源释放这件事,无论如何都别手写 finally 里一个个 close 了。用 try-with-resources,短短几行代码编译器帮你保证关闭顺序,用连接池时也保证归还。我接手过老代码,手写 close 又漏掉导致连接泄漏,最后生产环境连接数被打满,教训很深刻。

如果你现在正卡在某个报错上,先照着第 5 节核对一遍。版本、时区、SSL、字符集、网络,基本就这几大类。实在解决不了,把完整的异常堆栈贴给社区,比只扔一行「连不上」有用得多。JDBC 不难,细节多,多踩几个坑自然就熟了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询