直接说结论:mysql-connector-java和mysql-connector-j并不是两个不同的数据库驱动,而是同一个 MySQL Connector/J JDBC 驱动的两种 Maven 坐标。前者是老名字,后者是官方从 8.0.31 版本开始启用的新名字。旧坐标在 8.0.33 发布之后停止更新,后续的功能迭代、安全修复、新版本号体系全部落到了新坐标上。但驱动类名、JDBC 连接串、API 用法完全一致,绝大多数项目迁移时只需要改一下pom.xml或build.gradle里的依赖声明,代码一行都不用动。这篇文章我会把这两个名字的来龙去脉、实际差异、迁移步骤和我踩过的坑一次讲清楚,给正在做 Java + MySQL 开发的人一个能直接照抄的结论。
1. 先搞清楚:这两个名字指的是同一个驱动
1.1 Connector/J 在项目里到底扮演什么角色
MySQL Connector/J 是 MySQL 官方推出的 Java 版数据库驱动,实现的是 JDBC 规范。Java 程序要连 MySQL,无论你是用DriverManager.getConnection()裸连,还是走 Spring Boot + JPA/Hibernate、MyBatis、MyBatis-Plus,底层真正和 MySQL 实例建立 TCP 连接、发送协议包、解析返回结果的,就是这个驱动。简单说,它就是 Java 应用和 MySQL 之间的“翻译官”。
它的工作流程其实很直白:应用层通过连接池或DriverManager发起连接,驱动把 JDBC 调用翻译成 MySQL 客户端/服务端协议消息,通过网络发给 MySQL。连接起来之后,驱动负责处理认证、字符集、事务状态、结果集元数据这些琐碎但对正确性至关重要的东西。所以,驱动 jar 的版本对不对、依赖打包是否完整,直接影响连接稳定性、传参正确性和安全性。
很多人容易在这里被名字绕晕:mysql-connector-java看起来像“专门的 Java 驱动”,mysql-connector-j看起来像“某个新版本”。实际上它们背后是同一个项目、同一套源码、同一个版本号体系,区别只是发布到 Maven 中央仓库时用的坐标写法发生了变化。
1.2 命名演变的关键时间节点
我梳理了几个关键节点,理清楚之后就不会再混淆:
- 很长一段时间里,官方发布 Connector/J 用的都是
mysql:mysql-connector-java这个坐标,版本从 3.x、5.1.x 一路走到 8.0.x。 - 2022 年 10 月发布 8.0.31 时,官方宣布启用新坐标
com.mysql:mysql-connector-j,并在新坐标下同步发布版本。 - 旧坐标
mysql:mysql-connector-java最后一次发布是 8.0.33(2023 年 1 月),之后不再出新的版本。 - 后续的 8.0.34、8.1、8.2、8.3、8.4 LTS、9.x 等版本,都只以
com.mysql:mysql-connector-j发布。
换句话说,如果某个项目里写的还是mysql:mysql-connector-java:8.0.33,那已经是旧坐标的“绝版”版本;如果看到com.mysql:mysql-connector-j:8.4.0,这是新坐标下的长期支持版本。这两者本质是同一个驱动,只是时代不同、名字不同。
2. 表面与本质:坐标不同,功能几乎相同
2.1 一张表看懂 Maven 坐标差异
| 对比项 | 旧坐标 | 新坐标 |
|---|---|---|
| groupId | mysql | com.mysql |
| artifactId | mysql-connector-java | mysql-connector-j |
| 起始版本 | 很早(3.x 时代就有) | 8.0.31 |
| 旧坐标最终版本 | 8.0.33 | 仍在持续发布 |
| 驱动类名 | com.mysql.cj.jdbc.Driver | com.mysql.cj.jdbc.Driver |
| JDBC URL 前缀 | jdbc:mysql:// | jdbc:mysql:// |
| 对应项目 | MySQL Connector/J | MySQL Connector/J |
从表里能看到,功能层面它们没有“型号”上的差异。8.0.x 的驱动类名都是com.mysql.cj.jdbc.Driver,连接串格式也是jdbc:mysql://主机:端口/库名?参数。所以哪怕你直接把手头项目从旧坐标切到新坐标,只要驱动版本号没有跨大版本(比如从 5.1 直接跳到 8.x),Java 代码基本不需要改。
2.2 驱动类名和连接串为什么不用动
这里专门说一下“代码不用改”的原因。JDBC 4.0 之后,驱动 jar 通过META-INF/services/java.sql.Driver做服务发现,Java 运行时会自动加载驱动。很多项目里已经不需要再写Class.forName("com.mysql.cj.jdbc.Driver")这种代码了。但如果你保留了这行,注意 8.x 的类名是com.mysql.cj.jdbc.Driver,别和 5.x 时代的com.mysql.jdbc.Driver搞混。
这一点和坐标新旧没有关系:8.0.30 用旧坐标,类名是com.mysql.cj.jdbc.Driver;8.0.31 用新坐标,类名还是com.mysql.cj.jdbc.Driver。真正容易踩坑的是从 5.1.x 直接升到 8.x 的场景,类名从com.mysql.jdbc.Driver变成了com.mysql.cj.jdbc.Driver,配置里没改就会直接报ClassNotFoundException。
JDBC URL 同样不变,常见的写法是:
jdbc:mysql://localhost:3306/my_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/ShanghaiuseSSL/sslMode、serverTimezone、allowPublicKeyRetrieval、characterEncoding、rewriteBatchedStatements这些常用参数,在新旧坐标下语义完全一致,迁移时不需要调整。
2.3 真正的功能差异:依赖打包策略变了
如果只看坐标和类名,会觉得这纯粹是一次“改名运动”。实际上有一个值得关注的变化,就是依赖打包策略。
旧坐标下的mysql-connector-java并不是一个完全自洽的 jar。它的 POM 对外声明了第三方依赖,最典型的是com.google.protobuf:protobuf-java,这是 X DevAPI 也就是 MySQL X 协议能力需要用到的库。如果你的项目使用 X Protocol 连接(mysqlx://这种协议),用旧坐标时往往还要手动补 protobuf 依赖。更麻烦的是,当项目里其他组件也用 protobuf,且版本和驱动要求的版本冲突时,会出现各种奇怪的NoClassDefFoundError或者“方法找不到”之类的问题。
新坐标com.mysql:mysql-connector-j从 8.0.31 开始改成了“自包含”的打包方式:把 X Protocol 需要的 protobuf 等第三方类重新定位(relocate)后打进驱动 jar 里。这样带来的直接好处是:引入驱动时不再需要额外声明一堆间接依赖,jar 扔到 classpath 就能用;同一个应用里出现多个 protobuf 版本互相踩踏的概率也大幅降低。代价就是 jar 体积变大了一些,因为内部多了被重新定位的类。
用 Maven 查看依赖树能直观感受到这个差异。旧坐标的依赖树下会挂着com.google.protobuf:protobuf-java,新坐标的依赖树基本是干净的,几乎没有第三方 compile 依赖。这一点对于用mvn dependency:tree排查冲突的人来说,区别非常明显。
3. 为什么官方要改坐标:三个必须知道的背景
3.1 统一 Maven 命名空间
MySQL 官方在 Maven 上发布的 Java 相关构件不少,但历史上命名比较散。mysql-connector-java的 groupId 是mysql,看起来像是一个非官方组织或个人维护的构件。改成com.mysql之后,group 反域名命名规范和 Java 社区主流习惯一致,也能让使用者一眼看出这是官方发布的东西。这个变化本质上是一次“身份标识”的规范化,类似把一个项目从个人命名空间挪到官方命名空间。
从使用者的角度讲,groupId 变了对代码没有任何影响,但会让依赖管理更清晰。尤其是在大型多模块项目里,排查“这个 jar 到底是谁家的”会方便很多。
3.2 让驱动变成一个自包含构件
旧坐标时代的 Connector/J 虽然功能完整,但依赖没有被“隔离”。最典型的就是 protobuf。如果你只是用传统 JDBC 方式连 MySQL,可能根本感知不到 protobuf 的存在;但只要用到 X DevAPI,旧坐标就需要额外引入 protobuf-java,版本还得小心对齐。
新坐标从 8.0.31 开始把第三方类 relocate 进自身 jar,相当于把一个需要“外部零件”的套装变成了“拆箱即用”的成品。这个设计对两类人特别友好:一类是打 fat jar 或 shaded jar 的部署场景,少一个间接依赖就少一个冲突源;另一类是离线环境、内网私服环境,构件越自包含,拉依赖越省事。
3.3 弃用规则与版本节奏
MySQL 官方对 Connector/J 的版本节奏也有调整。以前 8.0.x 一路往下推,很多用户分不清“驱动版本”和“MySQL 服务器版本”的对应关系。现在 Connector/J 跟随 MySQL 的版本策略,分长期支持版(LTS)和创新版(Innovation)。LTS 版本如 8.4.x 适合生产环境,创新版本如 9.x 适合尝鲜和验证新功能。
旧坐标停止在 8.0.33,意味着这个坐标下不会再有新的安全补丁和新特性。如果你的项目还在锁定mysql:mysql-connector-java,并且安全扫描工具开始对旧版本报高风险项,通常的解决办法就是切换到新坐标、升级到最新 LTS 版本,而不是继续在旧坐标里找更高版本号。
4. 实操迁移:从旧坐标切到新坐标
4.1 Maven 项目替换步骤
在pom.xml里找到旧的依赖声明,把整段替换掉。旧写法:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency>新写法:
<dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.4.0</version> </dependency>替换后建议执行一次完整构建确认没问题:
mvn clean verify如果项目里还有别的模块或父 POM 引用了mysql:mysql-connector-java,可以用下面的命令查一下谁还在依赖它:
mvn dependency:tree -Dincludes=mysql:mysql-connector-java查出来之后,在对应间接依赖里加 exclusion,避免新旧两个 jar 同时出现在 classpath。
4.2 Gradle 项目替换步骤
Gradle 项目的改法更简单。旧写法:
implementation 'mysql:mysql-connector-java:8.0.30'新写法:
implementation 'com.mysql:mysql-connector-j:8.4.0'改完可以用下面命令检查依赖情况:
./gradlew dependencies --configuration runtimeClasspath | grep mysql重点确认runtimeClasspath里只出现一个 MySQL 驱动坐标。如果新旧坐标同时出现,说明有某个库通过传递依赖把旧坐标带进来了,需要在该库的依赖声明里排除mysql:mysql-connector-java。
4.3 Spring Boot 与 MyBatis 等框架中的迁移要点
如果你用的是 Spring Boot,连接配置里的driver-class-name不需要动,仍然是:
spring.datasource.url=jdbc:mysql://localhost:3306/my_db?useSSL=false&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=your_password spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver需要注意的是,Spring Boot 不同版本对 MySQL 驱动坐标的管理方式有差异。部分版本的依赖管理(dependency management)可能还在使用旧坐标mysql:mysql-connector-java并管理版本号。如果你在 Spring Boot 项目里手动覆盖驱动版本,要连坐标带版本一起改,只改 version 属性而坐标没换,并不会真正切到新构件。
用 MyBatis 或 MyBatis-Plus 的项目同理:SqlSessionFactory配置、Mapper XML 里的内容完全不受影响,改依赖坐标后重新启动应用验证连接即可。如果你用的是 JPA/Hibernate,注意方言配置还是org.hibernate.dialect.MySQLDialect这类,和驱动坐标无关。
4.4 连接参数兼容性检查清单
迁移后最容易出问题的其实不是坐标本身,而是连接参数。我列一个常用参数检查清单,照着核一遍基本不会翻车:
| 参数 | 说明 | 常见坑 |
|---|---|---|
useSSL/sslMode | 是否启用 SSL | 旧项目可能设置useSSL=false,新版本建议用sslMode=DISABLED或PREFERRED |
serverTimezone | 服务器时区 | 不设置可能报The server time zone value错误 |
allowPublicKeyRetrieval | 是否允许获取公钥 | MySQL 8 默认认证插件为caching_sha2_password,连接工具客户端可能需要true |
characterEncoding | 字符集 | 建议utf8或utf8mb4,避免中文乱码 |
rewriteBatchedStatements | 是否重写批量语句 | 批量插入性能优化常用,开启需确认 SQL 语义兼容 |
这些参数在新旧坐标下没有任何差异,迁移时不需要改,但如果你本来就是从一个很老的项目整体升级,这些才是真正需要花时间核对的点。
5. 踩坑实录与排查速查
5.1 新旧两个 jar 同时出现在 classpath
这是迁移过程中最常见的问题。某天项目里明明只声明了一个新坐标依赖,但启动时出现类似“重复的类定义”或者驱动加载异常的报错,十有八九是有个老库通过传递依赖把mysql:mysql-connector-java带进来了。
排查思路分两步:先用mvn dependency:tree -Dincludes=mysql:mysql-connector-java或 Gradle 的dependencyInsight找到来源,然后在对应的传递依赖上做排除:
<dependency> <groupId>org.example</groupId> <artifactId>some-legacy-lib</artifactId> <exclusions> <exclusion> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </exclusion> </exclusions> </dependency>排除之后重新构建,确认 classpath 里只剩com.mysql:mysql-connector-j。
5.2 旧坐标 + X DevAPI 时的 protobuf 报错
如果项目使用 X Protocol 连接(比如通过mysqlx://协议或者使用mysql-connector-j的部分高级模块),并且还停留在旧坐标,运行时会遇到类似NoClassDefFoundError: com/google/protobuf/...的报错。这是因为旧坐标下的 protobuf 依赖没有被内置,而你的运行环境里恰好没有这个包。
解决办法有两个:要么切换新坐标,让驱动自带的 relocate 版本生效;要么临时补一个兼容版本的com.google.protobuf:protobuf-java依赖。前者是长期方案,后者只是临时止血。我在实际项目里就遇到过 protobuf 版本和项目内其他组件冲突的情况,最后是直接切到新坐标解决,一劳永逸。
5.3 从 5.1.x 升级到 8.x 时的类名陷阱
这不是坐标新旧的问题,但升级过程中特别容易踩。5.1.x 的驱动类名是com.mysql.jdbc.Driver,8.x 是com.mysql.cj.jdbc.Driver。如果你把 8.x 的 jar 放进了 classpath,但配置里还写着旧的类名,启动时会直接报:
ClassNotFoundException: com.mysql.jdbc.Driver有些框架(比如老版本 Spring Boot 的自动配置)还会因为找不到驱动类而回退到猜测逻辑,导致连接失败。排查时优先检查driver-class-name、hibernate.connection.driver_class这类配置项。
5.4 迁移后连接报错的排查思路
坐标改完、构建通过,但应用启动时报连接异常,这时候按顺序排查会比较快:
- 确认 classpath 里没有旧坐标残留,用依赖树命令验证。
- 确认 URL 参数没写错,特别是
serverTimezone和useSSL/sslMode。 - 确认 MySQL 账号的认证插件。如果是
caching_sha2_password,且没有使用 SSL,则通常需要allowPublicKeyRetrieval=true。 - 清掉本地 Maven 仓库里的旧缓存,执行
mvn -U clean verify重新拉取。 - 如果应用服务器有多个模块,检查公共 lib 目录里是否有旧版本的驱动 jar。
5.5 常见问题速查表
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
ClassNotFoundException: com.mysql.jdbc.Driver | 驱动类名用了 5.x 写法 | 改为com.mysql.cj.jdbc.Driver |
The server time zone value ... | 缺少时区参数 | URL 加serverTimezone=Asia/Shanghai |
Public Key Retrieval is not allowed | 认证插件为 caching_sha2_password 且未允许获取公钥 | URL 加allowPublicKeyRetrieval=true |
| 启动时有类冲突告警 | 新旧坐标同时存在 | 排除旧坐标传递依赖 |
| X DevAPI 使用时报 protobuf 缺失 | 旧坐标未内置 protobuf | 切换新坐标或手动补依赖 |
| 依赖树出现两个 mysql 驱动 | 传递依赖引入旧坐标 | 在引入方排除旧坐标 |
6. 新项目怎么选版本:个人建议
6.1 版本号规则简要解读
MySQL Connector/J 现在的版本号规律可以简单理解成:8.0.x 是成熟稳定的长线版本,8.4.x 是官方标记的 LTS 长期支持版,9.x 这类是创新版本、迭代更快但维护周期可能更短。如果你的项目是生产系统,我建议优先考虑 LTS 系列;如果只是个人学习或验证新功能,用最新的创新版本也没问题。
这和我们选 MySQL 服务器版本是一个思路:生产环境求稳,测试环境可以激进一点。连接参数、驱动类名在不同版本之间是稳定的,所以即使后来想升级版本,改动成本也很小。
6.2 按场景选择依赖坐标
| 项目状态 | 推荐做法 |
|---|---|
| 全新项目 | 直接用com.mysql:mysql-connector-j,选 LTS 版本 |
| 老项目用旧坐标且没有异常 | 尽快切到新坐标,至少升级到 8.0.33 以上 |
| 安全扫描拦截旧坐标 | 升级到新坐标下受支持的最新 LTS 版本 |
| 旧项目暂时不能动构建 | 先记录技术债,安排窗口升级,不建议长期停留在旧坐标 |
6.3 最后分享一点实际体会
我在实际排查过的项目里,见过最让人头疼的情况不是坐标差异本身,而是项目里同时存在多个版本的 MySQL 驱动 jar,导致排查一个简单的连接问题花掉半天时间。所以我的习惯是:每接手一个 Java 项目,第一件事就是跑一遍依赖树,把所有数据库驱动的坐标、版本列出来确认一遍。这个习惯帮我在后续的升级和排障中省了很多事。
如果你正在做的是新项目,直接一步到位用新坐标;如果是老项目,把这次坐标切换当作一次顺手清理依赖的机会,改完依赖后顺便把多余的传递依赖、过时参数一并清理掉。驱动这个环节稳定了,后面查问题会轻松很多。