☰
mysql-connector-java 与 mysql-connector-j:JDBC驱动坐标变更与迁移指南
2026/10/6 3:40:23 网站建设 项目流程

mysql-connector-java 和 mysql-connector-j,乍看就像同一款驱动的两个版本,实际也确实是同一款驱动在两个阶段的官方命名。过去两年我几乎每星期都会在项目群里看到有人被这个坐标差异坑一把,尤其是从 Spring Boot 2 升级到 Spring Boot 3 前后,报错和依赖冲突特别集中。很多人第一反应是“j”是不是精简版、“java”是不是完整版,或者怀疑是新旧厂商之争,但真相没那么戏剧化,坑却非常实在。

我先把结论放前面:mysql-connector-java 是 MySQL 官方 JDBC 驱动在 8.0.30 及之前使用的发布名和 Maven 坐标,mysql-connector-j 是 8.0.31 及之后启用的新坐标和 JAR 命名。驱动内部的包名、核心类、连接方式没有任何革命性变化,驱动类依然是com.mysql.cj.jdbc.Driver,JDBC 地址依然以jdbc:mysql://开头。区别集中在坐标、JAR 文件名、演进节奏和维护状态上。如果你只是做业务开发,迁移成本通常很小;但如果你不重视这个差异,依赖冲突、驱动版本错乱、安全更新停更这类问题会一个个找上门。

1. 先搞清楚:这俩名字背后是什么东西

1.1 旧坐标与新坐标的完整对照

先看 Maven 坐标,这是大多数项目第一次接触这个差异的地方。传统写法是这样:

<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency>

从 8.0.31 开始,官方推荐的写法变成了:

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

对比一下就能发现,不止 artifactId 变了,连 groupId 也从mysql变成了com.mysql。也就是说,这不是“同一个 artifact 改个版本号”的关系,而是完全换了一套 Maven 定位。Gradle 场景也一样,旧的写法是implementation 'mysql:mysql-connector-java:8.0.30',新写法是implementation 'com.mysql:mysql-connector-j:8.4.0'。

下载下来的 JAR 文件名也跟着变了。旧坐标对应的文件通常是mysql-connector-java-8.0.30.jar,新坐标对应的是mysql-connector-j-8.4.0.jar。如果公司内部有私服、统一依赖管理平台,或者你在手工拷贝 JAR 进 Tomcat 的 lib 目录,这文件名差异很容易导致误判,以为引入了一个新驱动。

这里有个很容易误导人的地方:旧坐标在 Maven Central 上基本停在 8.0.30 之后就不再更新了。如果你继续用mysql:mysql-connector-java,哪怕只把版本号往大了写,也拉不到新版驱动,因为你写的 8.0.31、8.0.33、8.1.0 这些版本号在旧坐标下根本不存在。顶多能找到一个相同版本前缀的“同名不同家”的构件,但那不是 MySQL 官方发布的。

1.2 官方为什么要改这个命名

每次讲到这个差异,都会有人追问“好好的名字为什么要改”。站在官方角度看,这算是一次发布规范的收敛。早期的 Connector/J 在 Maven 仓库里用的 groupId 是mysql,artifact 名就是mysql-connector-java,二十年前这么定没什么问题,但放到今天的 Maven Central 体系里就有点乱了:mysql这个 groupId 太通用,看不出官方出品方,和市场里各种第三方 MySQL 驱动混在一起也不容易区分。

改用com.mysql:mysql-connector-j之后,groupId 直接体现官方域名体系,artifactId 也和官方下载站上的文件名一致。MySQL 官方分发站上的压缩包、独立 JAR 包,从 8.0.31 起就叫mysql-connector-j系列,不再突出“java”后缀。也就是说,Maven 坐标终于和实际二进制发布物对上了。

另一个很实际的原因是维护承诺。旧坐标停在 8.0.30,意味着使用旧坐标的项目拿到的是没有后续安全修复的驱动。MySQL Connector/J 这几年仍持续修复安全漏洞和兼容性问题,这些修复只发布在新坐标下。如果项目一直在用mysql:mysql-connector-java,等同于被动放弃这些更新。理解这一点之后再看迁移,就不是“为了迎合新习惯”,而是实打实的维护必要。

2. 名字不同,里面到底改没改?核心细节逐项拆

2.1 JAR 内部结构与驱动类

很多人害怕改坐标之后代码要跟着改,先说结论:不需要。驱动类名始终是com.mysql.cj.jdbc.Driver,包名一直是com.mysql.cj,JDBC URL 一直是jdbc:mysql://。不管你在代码里写Class.forName("com.mysql.cj.jdbc.Driver"),还是在配置文件里配置 driver-class,或者交给 Spring Boot 自动识别,所有原来能跑的地方在新坐标下依然能跑。

JAR 包内部有个重要的 SPI 文件路径:META-INF/services/java.sql.Driver。这个文件在旧坐标和新坐标里都指向com.mysql.cj.jdbc.Driver,所以 Java 的DriverManager可以自动发现驱动,不需要你手动Class.forName。JDBC 4 以后,这是判断一个连接器是否“符合现代加载方式”的关键。

不过要留意一个历史遗留类名:com.mysql.jdbc.Driver。如果你是从老项目继承的配置,经常会看到这个老类名。它在 Connector/J 8 里被保留了一段时间,但每次加载都会打印一行警示,提示你“这个类已经被弃用”。到了 Connector/J 9.x,这一类老包名里的类基本被清理了。如果你配置里写的是com.mysql.jdbc.Driver,换到新坐标下的高版本驱动时,轻则告警刷屏,重则直接报找不到类。趁迁移坐标的时候顺手改成com.mysql.cj.jdbc.Driver,是最省事的一步。

2.2 连接 URL 与配置项差异

连接串格式在新旧坐标下完全一致,常见写法长这样:

jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true

这里面的参数并不是坐标变化带来的,而是 MySQL 8 服务端引入caching_sha2_password默认认证插件之后,客户端必须面对的。尤其是allowPublicKeyRetrieval=true,在非 SSL 连接、密码认证使用缓存 SHA2 插件时经常需要显式开启,否则会报Public Key Retrieval is not allowed。很多人升级完坐标、换了驱动之后出现这个问题,第一反应是驱动坏了,其实这是认证机制变化导致的,和mysql-connector-j没关系。

serverTimezone=Asia/Shanghai也一样,执行SELECT NOW()或写入TIMESTAMP字段时,如果客户端和服务端时区不一致,会出现 8 小时左右的时间错位。这类问题不挑坐标,新旧版本都通用。我见过不少团队迁移坐标后从头排查性能问题,最后定位到是连接参数抄漏了一个。

2.3 版本、兼容性与支持差异

把新旧坐标的驱动放在一起看,核心功能没有变化,但版本节奏和维护状态差异明显。官方从 8.0.31 开始把安全补丁、JDK 17 适配、新协议特性等都放在新坐标下持续迭代,所以新坐标能获得更长周期的支持。

对比维度mysql-connector-javamysql-connector-j
Maven groupIdmysqlcom.mysql
Maven artifactIdmysql-connector-javamysql-connector-j
JAR 文件名mysql-connector-java-8.0.30.jar 等mysql-connector-j-8.0.31、8.4.0 等
驱动类名com.mysql.cj.jdbc.Drivercom.mysql.cj.jdbc.Driver
JDBC URLjdbc:mysql://jdbc:mysql://
维护状态停在 8.0.30,无后续安全修复持续发布修复和新版本
Spring Boot 3 支持不推荐,官方不再管理旧坐标Spring Boot 3 默认管理

版本兼容性层面,Connector/J 8.x 系列整体要求 JDK 8 起步,可以连接 MySQL 5.6、5.7、8.0 等常用版本。新坐标下的高版本驱动,比如 9.x,官方对 JDK 的要求会更高,对旧 MySQL 服务端的兼容范围也会收缩。如果你的部署环境是 JDK 8,稳妥选择是停留在 8.x 系列的较新版本,而不是盲目追新。这里我给的建议是:先看 JDK,再看 MySQL 服务端版本,最后选驱动版本。坐标变简单,版本选型不能跟着简单。

3. 从旧坐标切换到新坐标:迁移与实战配置

3.1 Maven 和 Gradle 的坐标替换

迁移第一步是把依赖坐标替换掉。Maven 项目直接改 pom.xml:

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

Gradle 项目改 build.gradle:

dependencies { implementation 'com.mysql:mysql-connector-j:8.4.0' }

版本号按需选择。我建议先到 Maven Central 看一眼com.mysql:mysql-connector-j最新正式版,不要凭记忆填版本。新坐标的版本号没有沿用旧坐标那种“8.0.30+1”的规律,比如它后面有 8.0.31、8.0.32、8.0.33、8.1.0、8.2.0、8.3.0、8.4.0、9.x 这些版本,一眼看过去像跳跳糖一样。如果不看仓库真实版本,很容易写一个不存在的版本号。

替换完之后,在 IDEA 里刷新 Maven,或者执行依赖树命令确认没有旧坐标残留:

mvn dependency:tree -Dincludes=mysql:mysql-connector-java mvn dependency:tree -Dincludes=com.mysql:mysql-connector-j

第一个命令如果输出为空,说明项目里已经没有旧坐标的直接依赖;如果还有,继续排查传递依赖。第二个命令可以看到实际引入的新坐标版本。这个验证步骤很关键,我见过有人在 pom 里改了坐标,但公司内部的 BOM 或父 pom 里还锁着旧坐标,最终打包时旧驱动还是被带了进去。

3.2 Spring Boot 的依赖管理处理

如果你用的是 Spring Boot,迁移会简单很多,但也要理解背后的依赖管理逻辑。Spring Boot 2.x 系列对 MySQL 连接器的默认管理,一直沿用旧坐标mysql:mysql-connector-java。你在 Spring Boot 2 项目里只写spring-boot-starter-jdbc或spring-boot-starter-data-jpa,再配合对应的 starter,依赖里可能已经带上了 MySQL 驱动,不需要手动写坐标也能连上数据库。

Spring Boot 3.0 之后,官方把默认 MySQL 驱动坐标换成了com.mysql:mysql-connector-j。这意味着如果你把项目由 Spring Boot 2 升到 Spring Boot 3,大概率不需要自己改坐标,升级过程中 Boot 自己的 dependency management 已经帮你换到了新坐标。

但有个隐藏问题容易踩:你的项目里如果显式写了旧坐标,并且用了<version>或者公司父 pom 锁了版本,Spring Boot 的托管版本可能不会覆盖你的显式声明,最终出现“Spring Boot 3 已经切到新坐标,但你的项目里还拎着旧坐标”的混合状态。典型现象是:运行时不报错,但你用mvn dependency:tree看依赖树,会看到两个 MySQL 驱动坐标同时存在。这种状态短期可能没问题,长期维护起来特别容易懵。

我的处理习惯是:升级 Spring Boot 版本时,顺手全局搜索项目里所有mysql-connector-java字样,只要不是历史文档,统一改成新坐标。不要相信“反正在升级框架,依赖会跟着变”这种话,显式依赖最可靠。

3.3 构建验证与运行验证

坐标改完之后,光编译通过还不够,要验证运行时确实加载了新驱动。一个非常直观的办法是打印驱动元信息:

import java.sql.Connection; import java.sql.DriverManager; import java.sql.DatabaseMetaData; public class DriverCheck { public static void main(String[] args) throws Exception { Class.forName("com.mysql.cj.jdbc.Driver"); try (Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=Asia/Shanghai", "root", "yourpassword")) { DatabaseMetaData meta = conn.getMetaData(); System.out.println("DriverName = " + meta.getDriverName()); System.out.println("DriverVersion = " + meta.getDriverVersion()); } } }

正常输出会显示类似MySQL Connector/J和mysql-connector-j-8.4.0的信息。如果输出的版本号还是旧版本段,说明 classpath 里仍有旧驱动优先级更高,需要继续排查依赖顺序。

还有一种更直接的检查方式,把 JAR 包解压开看META-INF/MANIFEST.MF。新旧 JAR 的Implementation-Title和Implementation-Version会有差异,新版本包里能明显看到新 artifact 信息。对大型项目来说,这种低层验证比看 pom 更可靠,因为打包产物可能经过各种插件重写路径。

4. 实操中容易踩的坑与排查实录

4.1 版本冲突和传递依赖

最常见的坑就是旧坐标和新坐标同时出现在 classpath。为什么会出现?两个原因:一是项目自身显式声明了旧坐标,同时还被某个框架通过 BOM 带入了新坐标;二是多个模块各自管理依赖,有的模块用旧坐标,有的模块用新坐标,合并到最终应用时两个 JAR 都在。

这种双 JAR 情况最麻烦的是,新旧坐标的包名和类名完全一致,JVM 加载哪个类取决于 classpath 顺序。你无法保证旧 jar 里的com.mysql.cj.jdbc.Driver一定会被新 jar 覆盖。如果旧 jar 在前,应用加载到的驱动就是旧版本,代码上没有任何编译期报错,但运行时可能带着旧版本已有的安全漏洞去连接数据库。

排查思路很固定:先mvn dependency:tree,把输出重定向到文件里,同时搜mysql-connector-java和mysql-connector-j两个关键词。看到两个坐标同时存在,就沿着依赖路径找到父模块,把其中一个的exclusion排除掉,或者统一管理版本。我一般倾向保留新坐标、排除旧坐标,因为旧坐标已经没有后续修复。

在 Gradle 项目里同样可以用dependencies报告命令查看最终解析结果,如果出现了两个 JAR,还需要看 resolution strategy 是否导致版本冲突。Gradle 对模块元数据里的冲突处理规则和 Maven 不完全一样,不能只参考 Maven 的经验。

4.2 驱动类名和自动加载问题

第二类坑集中在驱动加载。报错一般长这样:

java.sql.SQLException: No suitable driver found for jdbc:mysql://...

如果你的代码还在用老类名com.mysql.jdbc.Driver,而且驱动版本很新,可能直接抛ClassNotFoundException。虽然 Connector/J 8 的老类名在很长一段时间内还能用,只是打警告,但高版本里已经删除了老类名,继续依赖老类名等于给自己埋雷。

如果用的是 JDBC 4 的自动加载机制,也就是依赖META-INF/services/java.sql.Driver自动注册驱动,那么项目里不要写Class.forName("com.mysql.cj.jdbc.Driver")也不会出问题。但有些老框架,比如部分第三方连接池组件,会自己读驱动类名去加载。这种场景下把系统属性或配置文件里的驱动类改成com.mysql.cj.jdbc.Driver就够了。

另外要注意应用服务器的 classloader。Tomcat 下如果驱动 JAR 放在lib目录而应用里又带了一份,容易出现“两个驱动实例、两种注册状态”的怪问题。驱动类加载后注册到DriverManager的实例,和应用加载的驱动如果不是同一个 classloader 里的类,可能会互相找不到。遇到“应用能看到 jar,但连接池就是加载不了”的情况,检查一下驱动 JAR 是不是重复部署。

4.3 认证与时区问题

换新坐标之后突然报认证相关错误,最容易让人误判。MySQL 8 默认的caching_sha2_password认证插件要求客户端驱动支持对应的握手协议和公钥获取流程。如果你用的还是旧坐标停在 8.0.30 的版本,有些场景下和服务端兼容会有问题;换到新坐标高版本驱动后,这类问题通常会消失,但如果连接串里没有allowPublicKeyRetrieval=true,启动时依然可能报:

Public Key Retrieval is not allowed

这个错误和坐标迁移无关,是连接参数缺失导致。解决方案是在 JDBC URL 后面追加allowPublicKeyRetrieval=true。如果你已经在用 SSL 连接,通过安全通道交换公钥的风险可控;如果是内网开发环境,问题也不大。生产环境则建议优先启用 SSL,再考虑开这个参数。

时区问题也很常见。迁移坐标后,应用服务器和数据库服务器的时区如果一致,不会有事;一旦不一致,尤其是跨国机房或云主机和自建库在不同地域,TIMESTAMP字段容易出现几小时的偏差。这时要在连接串里明确指定serverTimezone=Asia/Shanghai,或者把数据库连接池里的初始化 SQL 设置好会话时区。不要靠系统默认时区碰运气,云环境经常默认 UTC。

4.4 升级到高版本前的选型清单

最后给一张我在实际项目中使用的选型清单。每次替换坐标前,按下面顺序过一遍,能挡掉大部分坑:

决策点检查内容建议
JDK 版本JDK 8、11、17 还是更高JDK 8 优先选 8.x 系列新版本,JDK 17 可尝试 8.4/9.x
MySQL 服务端版本5.6、5.7、8.0 还是 8.4旧服务端尽量用 8.x 驱动,不要跳到 9.x
Spring Boot 版本2.x 还是 3.x显式替换成新坐标,别和 Boot 默认管理打架
连接池类型HikariCP、Druid、Tomcat JDBC核对驱动类名和连接串参数
部署环境 JDK 目录jar 是否被手动复制确认没有旧 jar 残留

按这张表走一遍,基本不会出现“迁移完连不上”的场面。我自己处理过的案例里,九成问题出在三处:坐标混搭、驱动类名旧、连接参数缺项。三者都不是什么高深难题,但排查起来很耗时间。

4.5 一个我推荐的标准配置范本

这里放一套我常用且实测稳定的配置,你可以直接抄走。Maven 坐标部分选择com.mysql:mysql-connector-j的 8.4.0 版本,连接池用 HikariCP,JDBC URL 用下面这个范本:

jdbc:mysql://你的数据库地址:3306/库名?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true

rewriteBatchedStatements=true是容易被忽略但收益很大的参数,批量插入走 JDBCaddBatch时能明显提升吞吐。不过它也不是万能的,遇到复杂ON DUPLICATE KEY UPDATE和批量更新混用时,要注意 SQL 语义是否符合预期。

代码里统一使用com.mysql.cj.jdbc.Driver,不要再用老类名。配置 HikariCP 时,driverClassName或dataSourceClassName二选一即可,前者适合明确指定驱动类,后者适合直接用连接器的MysqlDataSource实现。用dataSourceClassName时要注意,部分参数要通过dataSourceProperties透传,比如serverTimezone、useSSL这些,直接写在 JDBC URL 里反而不生效。这个细节很多人踩过。

还有一点经验:把驱动 JAR 从 lib 目录手工管理改成 Maven/Gradle 统一管理之后,再也不要往应用服务器的 lib 目录里复制驱动。双份驱动带来的 classloader 问题非常隐蔽,而且一旦出问题,日志往往不会直接提示“duplicate driver”,而是各种诡异的连接失败。统一只用一份驱动,是这个环节最好的实践。

这套迁移做完之后,你会发现业务代码一行都不用动,连接串、驱动类、配置项保持原样,只是把依赖声明和 JAR 生命周期从旧坐标迁到新坐标。从mysql-connector-java到mysql-connector-j,表面上是名字变短了一点,实际上是从一个“历史遗留坐标”迁移到“持续维护坐标”。以后再加依赖、升级版本、排查安全公告,都认准新坐标,省心很多。我个人在做这类迁移时的习惯是先跑一次全链路连接测试再收工,别只看编译通过,数据库能连上才算完事。

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

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

立即咨询