ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MySQL 8.0 JDBC驱动jar包选型与连接配置避坑指南

MySQL 8.0 JDBC驱动jar包选型与连接配置避坑指南 上周帮一个项目排查数据库连接问题现象很奇怪代码在本地IDEA里跑得好好的一打包部署就报No suitable driver found。查了半天问题出在一个细节上——IDEA里编译时能看到 jar 包但 Maven 打包时根本没把依赖带进去。类似的坑我在 MySQL 从 5.7 升到 8.0 之后见过不下十次而每次的源头几乎都指向同一个东西MySQL 8.0 版本 JDBC 驱动 Jar 包远没有看起来那么简单。网上搜MySQL 8.0 JDBC Jar包跳出来的下载链接五花八门版本号有 8.0.12、8.0.33、8.4.0坐标有mysql:mysql-connector-java也有com.mysql:mysql-connector-j。新手很容易直接下载一个塞进项目然后被Public Key Retrieval is not allowed、时区异常、ClassNotFoundException轮番问候。这篇内容我就按自己实战中拆过的一堆问题来讲覆盖驱动版本选型、连接串配置、三种常见项目的落地方式以及打包上线阶段最容易翻车的几个点。适合刚接触 MySQL 8.0 的开发者快速上手也适合已经把项目跑起来但偶尔被连接问题折磨的人对照排查。1. 先弄明白驱动包和 MySQL 实例的配对关系1.1 官方支持矩阵驱动版本为什么不能只看8.0MySQL 官方把 JDBC 驱动叫做 Connector/J8.0 系列对应的版本号形如8.0.33、8.0.36。它跟 MySQL 服务器版本不是严格一一对应的并不是说 MySQL 是 8.0.36 就必须用 8.0.36 的驱动。官方支持矩阵说明Connector/J 8.0.x 可以连接 MySQL 5.7 及以上版本包括 MySQL 8.0 和 8.4。反过来MySQL 8.0 服务器如果搭配 5.1.x 的老驱动会直接遇到Client does not support authentication protocol requested by server的认证失败因为 MySQL 8.0 默认的认证插件是caching_sha2_password老驱动根本不认识。我的建议是新项目直接选 8.0.33 或 8.0.36 这个段位的版本不要选太老也不要追太新。8.0.33 在社区里用得最多踩坑记录相对完备8.0.36 以后修复了一些连接池场景下的边界问题但整体差异很小。太老的版本比如 8.0.12对 MySQL 8.0 新增变量和认证机制的适配不够到位容易出现莫名其妙的问题。如果你用的是 MySQL 8.4也完全可以继续用 Connector/J 8.0.36没必要非上 8.4 的驱动。另外要留意驱动的 JDK 要求。Connector/J 8.0 要求JDK 8 及以上如果你的项目还在用 JDK 7那就得先考虑升级否则驱动编译版本都不兼容。这属于选型阶段就要确认的硬约束别等代码写完再找原因。1.2 同一版本号下的两个坐标Maven 命名迁移这是最容易把人绕晕的地方。早期在 Maven 中央仓库搜 MySQL JDBC 驱动大家熟悉的坐标是dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency但从 8.0.31 开始官方把坐标迁移到了com.mysql下artifactId 也改成了mysql-connector-jdependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId version8.0.33/version /dependency两个坐标在中央仓库里都能搜到版本号也相近但语义上旧坐标已经进入维护停滞状态新坐标才是继续演进的方向。如果一个团队里有人用了mysql:mysql-connector-java另一个人用了com.mysql:mysql-connector-j在 Maven 依赖传递时可能出现两个 jar 同时进入 classpath。虽然类名冲突不明显但版本不一致会带来一些很难排查的运行时差异我在后面会专门讲到。手动下载的场景也一样官网下载页提供的压缩包在 8.0.31 之前和之后的命名有变化解压后主 jar 文件是mysql-connector-j-8.0.33.jar。如果你从第三方jar 包下载站下载一定要留意文件大小和校验信息我曾经见过被二次打包的精简版驱动缺失了com.mysql.cj.jdbc.Driver类的一部分内部依赖连 MySQL 8.0 的 TLS 握手都完不成。1.3 下载与校验别从不可信站点拿 jar 再扔进生产拿到 jar 之后第一件事不是往项目里塞而是确认它真的是官方产物。可以到 MySQL 官网或者 Maven Central 下载然后用jar tf命令看一眼关键类是否存在jar tf mysql-connector-j-8.0.33.jar | grep com/mysql/cj/jdbc/Driver.class正常输出应该是com/mysql/cj/jdbc/Driver.class如果这个类不存在说明拿到的 jar 多半不完整或者被改过坚决不要用。还可以在 Maven 中央仓库页面找到官方发布的 SHA-256 值与本地文件比对这一步在离线环境和生产部署时尤其管用我习惯把校验值直接记到项目的 README 里。2. 连接串和驱动类名的变化读懂报错背后的设计逻辑2.1 驱动类名变更从com.mysql.jdbc.Driver到com.mysql.cj.jdbc.DriverMySQL 5.x 时代大家写数据库连接时都喜欢来一句Class.forName(com.mysql.jdbc.Driver)或者直接在 JDBC URL 里配上driverClassNamecom.mysql.jdbc.Driver。到了 8.0官方把核心代码包重构到了com.mysql.cj.jdbc下面推荐的驱动类名变成了com.mysql.cj.jdbc.Driver那旧类名还能不能用Connector/J 8.0 里其实保留了一个兼容壳类com.mysql.jdbc.Driver它继承了新驱动类所以你写老名字在大多数情况下也能初始化成功。但我不建议继续用原因很简单保留类只是过渡方案而且一旦你混用了连接池自动识别逻辑老类名在某些框架里会触发额外的 SPI 扫描日志里出现一堆奇怪的告警。顺便解释一下No suitable driver found是怎么来的。JDBC 4.0 之后驱动支持SPI 自动注册机制DriverManager会在启动时通过META-INF/services/java.sql.Driver自动加载 jar 包里的驱动类不需要你手动Class.forName。报No suitable driver本质上是DriverManager没找到能识别当前 JDBC URL 的驱动。最常见的三种原因驱动 jar 根本不在 classpath 里、驱动类名写错、JDBC URL 前缀写错。我见过有人把jdbc:mysql://写成mysql://DriverManager 自然匹配不上。2.2 三个参数解决三个历史包袱serverTimezone、useSSL、allowPublicKeyRetrievalMySQL 8.0 时代连接串肉眼可见地变长了。这真不是大家愿意堆参数而是有几个历史包袱必须处理。第一个是时区。MySQL 8.0 服务器默认的time_zone变量往往指向SYSTEM而驱动在建立连接时需要解析服务器时区如果解析不了就会抛The server time zone value йʱ is unrecognized or represents more than one time zone解决方案有两种。一是连接串里显式指定serverTimezoneAsia/Shanghai这个参数的作用是告诉驱动别去猜服务器时区直接用这个。我推荐这种做法而不是去改服务器全局时区——你未必有权限动生产库而且同一台实例上可能跑着其他依赖默认时区的应用。第二个是 SSL。MySQL 8.0 默认开启 SSL 相关选项驱动在连接时会尝试做 TLS 握手。如果服务器证书不可信或者自签名连接就会失败或者抛出证书告警。开发环境或内网可信网络里最直接的处理是关掉 SSL 校验useSSLfalse如果是公网环境那我建议老老实实把证书链配上不要因为图省事关闭加密。安全边界不同处理方式不能一刀切这点我在第 4 章还会提到。第三个是allowPublicKeyRetrievaltrue。这个参数跟 MySQL 8.0 的默认认证插件caching_sha2_password直接相关。在没有 SSL 的情况下客户端需要用服务器的 RSA 公钥来加密密码传输而公钥默认不会主动下发驱动就会抛Public Key Retrieval is not allowed简单理解服务器说我可以给你一把锁公钥但你要先申请驱动默认不允许这个申请动作于是两边僵住了。设置allowPublicKeyRetrievaltrue就是允许驱动去取这把锁。很多初学者第一次遇到这个报错时以为是密码错折腾半天才发现是少一个参数。2.3 连接串模板和连接池参数位置综合上面三个因素我平时在项目里使用的标准连接串是这样jdbc:mysql://127.0.0.1:3306/test_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrueuseUnicodetruecharacterEncodingutf8是为了确保字符集不乱码属于老生常谈但建议保留。要注意的是在 Spring Boot、MyBatis、HikariCP 这些框架里参数要放在JDBC URL 里而不是放在数据源配置的独立属性里。比如在用 HikariCP 的时候有人会把serverTimezone写成一个独立的dataSource属性结果发现根本不生效因为 HikariCP 只把它当作驱动属性透传没走 URL 解析器。最省心的做法URL 里直接把参数带全连接池层面只负责分配连接不要让它干预 MySQL 方言的参数解析。顺带一提Flink 项目里的 JDBC 连接器也是同一个道理。flink-connector-jdbc的 with 参数里指定url时同样需要把这些驱动参数原样放进 URL。热词里提到的 Flink JDBC 连接器异常半数以上都是驱动 jar 没进入 Flink 的运行时 classpath或者是连接 URL 缺少了上面说的几个参数。前者我放到第 3 章讲后者记住一句话URL 是驱动参数的唯一入口。3. 三个最常见的落地场景把 Jar 包真正变成可运行应用3.1 手动导入非 Maven 项目的标准姿势如果项目不是 Maven/Gradle 管理而是传统方式纯手工维护 classpath那就绕不开手动导入。我以 IDEA 为例路径是打开Project Structure快捷键CtrlAltShiftS。进入Modules面板选中当前模块。切到Dependencies页签点击右下角的选择JARs or directories。找到mysql-connector-j-8.0.33.jar加入后确认勾选Compile和Runtime范围。最后点Apply别直接关弹窗否则不会生效。这里最容易被忽略的是导入之后IDEA 会用依赖顺序里的某一个模块输出目录覆盖你的 lib 配置。如果项目里同时存在lib/目录下的旧驱动IDEA 的依赖列表里会出现两个驱动条目运行时实际生效的是排在前面那个。怎么确认看Dependencies页签里的排序把自己想要的 jar 移到Order/Export靠上位置或者干脆把旧驱动条目删掉。另外Module级别的依赖和Project级别的依赖是两个概念。如果你在 Project Structure 里只加到了 Project 的 Libraries 里但模块没有引用这个 Library照样运行不起来。很多人查了半天 classpath 没问题最后发现是加错了层级。3.2 Maven/Gradle用坐标代替 jar 文件能用 Maven 或 Gradle 管理的项目就别手动下载 jar 了。Maven 的好处不仅是自动拉依赖还能帮你解析传递依赖、管理版本冲突。pom.xml 里加这一段就行dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId version8.0.33/version /dependencyGradle 对应implementation com.mysql:mysql-connector-j:8.0.33有的团队还在用旧坐标mysql:mysql-connector-java虽然也能下载但既然官方已经迁移我倾向直接用新坐标。新坐标下Spring Boot 2.7 以上的版本在spring-boot-starter-parent里已经帮你管理了版本号你可以不写version让 Spring Boot 的依赖管理生效。比如 Spring Boot 3.x 默认管理的mysql-connector-j版本会随 Boot 版本走通常是 8.0.3x 或更高。这里必须提醒一个反直觉的点在 Maven 项目里不要手动把 jar 下载下来再丢进模块依赖。Maven 项目一旦执行Reload All Maven ProjectsIDEA 会根据 pom 重新解析依赖你手动添加的 jar 会从依赖列表里消失。正确的做法是本地仓库里没有的 jar 交给 Maven 下载哪怕公司内网不能访问外网也应该配置 Maven 私服镜像而不是绕过管理机制手工加包。否则同事拉取代码后项目构建结果跟你的完全不一样。3.3 Spring Boot 与 Flink框架集成场景的特殊点Spring Boot 整合 MySQL 驱动非常成熟配置层面在application.properties里写spring.datasource.urljdbc:mysql://127.0.0.1:3306/test_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue spring.datasource.usernameroot spring.datasource.password123456 spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driverdriver-class-name其实可以省略因为 Spring Boot 会根据 URL 的jdbc:mysql前缀自动推断。但我在老项目迁移时习惯保留原因是可以避免某些自定义数据源初始化逻辑提前加载驱动导致顺序问题。Spring Boot 打包用的是spring-boot-maven-plugin它的repackage目标会把依赖 jar 全部塞进最终的可执行 jar 的BOOT-INF/lib目录。打包完成后可以用命令验证驱动真的在里面jar tf target/xxx-0.0.1-SNAPSHOT.jar | grep mysql-connector输出里能看到mysql-connector-j-8.0.33.jar才说明驱动被正确打进去了。Flink 的场景稍有不同。Flink 的 JDBC 连接器本身只是建立了一个数据源抽象不会帮你把 MySQL 驱动带进去。本地开发时连接器能跑是因为 IDE 的 classpath 里有驱动提交到集群就不行了因为作业节点的 classpath 没有。处理方式是二选一把驱动 jar 放到FLINK_HOME/lib目录下或者提交任务时通过参数指定flink run -c com.example.Job -j mysql-connector-j-8.0.33.jar ...很多Flink JDBC 连接器异常的帖子本质上就是在说这个报错信息大多是ClassNotFoundException: com.mysql.cj.jdbc.Driver。解决方案简单但没经历过的人会在集群和 IDE 之间反复横跳。3.4 一个最小可运行示例最后给一段最简单的验证代码适合在接入框架之前先用原生 JDBC 确认驱动和环境没有问题import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; public class JdbcCheck { public static void main(String[] args) throws Exception { String url jdbc:mysql://127.0.0.1:3306/test_db ?useUnicodetruecharacterEncodingutf8 serverTimezoneAsia/Shanghai useSSLfalseallowPublicKeyRetrievaltrue; try (Connection conn DriverManager.getConnection(url, root, 123456); Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(SELECT VERSION())) { rs.next(); System.out.println(MySQL version: rs.getString(1)); } } }如果这段代码能跑通说明 jar 包、驱动类、连接串参数都没问题。后面问题出在哪一层就逐层去查不要一上来就怀疑驱动坏了。4. 打包与上线阶段的高频异常排查4.1 打包后驱动消失的三层原因部署环境不同于本地驱动 jar 是否存在、是否被加载都需要重新验证。我把见过的高频问题整理成三条排查链路。第一层打出来的 jar/war 里有没有驱动。Spring Boot 可执行 jar 用jar tf查看BOOT-INF/lib传统 war 包查看WEB-INF/lib普通 Maven jar 如果没用maven-shade-plugin或spring-boot-maven-plugin做重打包依赖默认不会打进去。很多人以为在 IDEA 里点一下Build Artifact就是打包实际那只打了项目自身代码依赖全在外层。这时候部署上去跑必然NoClassDefFoundError。第二层驱动在 classpath 里但被排除了。Spring Boot 的spring-boot-maven-plugin默认排除了一些公共库这一般不会影响 mysql 驱动。更多的情况是 pom 里有 exclude 直接把mysql-connector-j排掉了。用 Maven 的依赖树命令查最直观mvn dependency:tree -Dincludesmysql:mysql-connector-java,com.mysql:mysql-connector-j第三层类冲突导致加载了错误版本。如果项目里的依赖同时引入了两个版本的驱动JVM 实际加载哪个由 classpath 顺序决定而不由你主观指定。你以为是 8.0.33实际跑到的是 8.0.12行为自然诡异。用下面的命令看看有没有两个mvn dependency:tree -Dincludesmysql,com.mysql只要有mysql:mysql-connector-java和com.mysql:mysql-connector-j同时出现就要处理掉一个。4.2 高频报错对照表我把这段时间高频出现的报错和对应处理整理成了表格方便大家快速对照报错信息根因处理方式No suitable driver found for jdbc:mysql://驱动未加载或 URL 前缀错误检查 classpath、驱动类名、URL 开头是否为jdbc:mysql://ClassNotFoundException: com.mysql.cj.jdbc.Driver驱动 jar 未在运行时 classpath检查 fat jar 的BOOT-INF/lib或 Flink 的--jarsPublic Key Retrieval is not allowed使用caching_sha2_password且未允许取公钥连接 URL 加allowPublicKeyRetrievaltrueThe server time zone value ... unrecognized驱动无法解析服务器时区URL 加serverTimezoneAsia/ShanghaiClient does not support authentication protocol驱动版本过老不认识 8.0 认证插件升级到 Connector/J 8.0 或调整账号认证插件Unknown system variable query_cache_size老驱动使用旧 SQL 检查项探活换新驱动即可Communications link failure网络不通、端口被防火墙拦截或服务器未监听用 telnet 测端口、检查 bind-addressCould not create connection to database server驱动类名配错或初始化失败确认driver-class-name写法推荐用日志定位原始异常表格里的前三个是 MySQL 8.0 时代最典型的组合拳我几乎每接待一个从 5.7 迁移到 8.0 连不上的同事都要把这几个参数补一遍。补完基本就通了端口和账号问题反而通常在一开始就被排除了因为本地都是能连的。4.3 依赖传递覆盖两个 Mysql jar 共存的隐患前面提到新老坐标并存的问题这里展开说说。场景是这样的你的工程里引入了com.mysql:mysql-connector-j:8.0.33但某个内部公共依赖自动带进来了mysql:mysql-connector-java:8.0.12两者都进入 classpath。由于两个坐标的包结构高度重叠Maven 不会把它们当成冲突于是你的应用里同时存在两份驱动实现。后果是什么驱动类有同名但版本逻辑完全不同。Spring Boot 的数据源自动配置在扫描 Driver 实现的时候可能命中旧版本此时 8.0 新增的参数配置行为就无法生效出现一些配置明明写了却没效果的诡异现象。比如allowPublicKeyRetrieval在新驱动里默认更严格在旧驱动里可能行为就不同。处理手段是在 pom 里把旧坐标排除掉dependency groupIdcom.example/groupId artifactIdinternal-common/artifactId version1.0.0/version exclusions exclusion groupIdmysql/groupId artifactIdmysql-connector-java/artifactId /exclusion /exclusions /dependency做完之后再次跑mvn dependency:tree确认最终只保留一个驱动坐标。这个检查动作我建议放到每次发布前的检查清单里成本极低收益极高。4.4 5.x 驱动访问 8.0 的误判运维视角下还有一个容易误判的场景账号权限和密码都正常却报认证失败。原因在 MySQL 8.0 把默认认证插件改成了caching_sha2_password而 MySQL 5.7 时代的驱动mysql-connector-java:5.1.x只支持mysql_native_password。于是客户端和服务器在握手阶段直接谈崩。这类问题的正确解法是升级驱动到 8.0而不是把数据库账号改回旧认证插件。有些文章会教你把用户改成mysql_native_password来临时过渡ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY password;这个操作在开发环境确实能快速解除阻塞但我不推荐把它当成长期方案。MySQL 8.0 之后的新版本已经把mysql_native_password标记为弃用未来版本里默认行为只会更倾向新认证插件。你今天为了兼容旧驱动把账号降级回去等到数据库小版本升级时可能又要踩一遍。正确的姿势是一次性把驱动和 JDK 升级到位别在认证插件层面做补偿。5. 把坑前置从选型入口就规避后续问题5.1 一张选型小矩阵聊了这么多本质上都是为了在选型阶段把问题消灭掉。我个人在项目初始化或者老项目迁移时会先对着下面这个矩阵过一遍环境条件推荐驱动版本补充说明MySQL 5.7 JDK 8com.mysql:mysql-connector-j:8.0.338.0 驱动兼容 5.7无认证插件障碍MySQL 8.0 JDK 8~17com.mysql:mysql-connector-j:8.0.33或8.0.36注意连接串补全四个参数MySQL 8.4 JDK 17com.mysql:mysql-connector-j:8.0.36或更新的8.4.x驱动使用caching_sha2_password确认公钥检索配置老项目被迫用 JDK 7只能5.1.x但连 8.0 会认证失败优先升级 JDK不要改认证插件妥协这个矩阵的核心逻辑不是最新就是最好而是驱动版本要能覆盖它所连接的服务器版本且支持服务器端默认认证方式。你在选型时多花十分钟确认这三点后面能省下好几个小时的排查时间。5.2 部署前核对清单再分享一份我压箱底的核对清单发布前照着检查一遍驱动坐标统一使用com.mysql:mysql-connector-j没有历史遗留的mysql:mysql-connector-java混入依赖树。连接 URL 补齐serverTimezone、useSSL、allowPublicKeyRetrieval和characterEncoding。打包产物里能看到驱动 jarSpring Boot 检查BOOT-INF/libwar 检查WEB-INF/libFlink 任务确认--jars参数。生产库账号使用的是caching_sha2_password而不是为了兼容旧驱动被降级成mysql_native_password。公网环境下没有使用useSSLfalse而是配置了可信证书链。本地开发环境和测试环境的数据源配置分开管理不要把生产库的账号明文写在配置文件里。这份清单是面向稳定运行的底线不是面向能连上就跑的最低要求。很多人栽跟头就是只追求最后一行代码能跑通忽略了前面那些结构性问题。5.3 离线环境与驱动备份最后说一个偏运维的实操心得如果你的生产环境无法访问外网Maven 中央仓库也连不上一定要提前在本地仓库或自建私服里固化驱动 jar。内网私服的镜像配置基本是必备动作mirror idnexus/id mirrorOf*/mirrorOf urlhttp://nexus.internal.example.com/repository/maven-public//url /mirror没有自建私服的团队至少要在构建机的本地仓库.m2/repository/com/mysql/mysql-connector-j/8.0.33/下保留一份完整 jar。否则哪天有人清理了构建机缓存或者新同事第一次拉代码构建就会在下载依赖这一步卡住。顺手把 SHA-256 校验值记在 README 里能避免内网传输过程中文件被破坏的问题。我个人在实际项目里的体会是MySQL 8.0 的 JDBC 驱动从下载、引入、配置到打包每一步都有标准的不踩坑姿势。驱动 jar 本身只是个几十 MB 的文件但它跟服务器版本、JDK 版本、认证插件、连接池框架之间的关系才是决定项目是否稳定运行的关键。你在新项目里第一次配数据库连接时花半小时把这些参数和依赖关系理顺后面遇到任何连接异常都能很快在报错信息里找到对应答案不会再把时间浪费在是不是 jar 包下载错了这种无效排查上。如果非要说一个最值得养成的小习惯那就是每次连接 MySQL 8.0 之前先看一眼连接 URL 里的三个参数是否齐全。时区、SSL、公钥检索这三个参数齐了大多数连接问题就已经解决了大半。剩下的网络、账号问题用日志和端口探测都能快速定位。驱动和连接串是一门配置前置的功夫前置做得好运行期就很少需要大动干戈。
返回列表