先说一个我上个月亲手排查过的案例。朋友接手了一个老项目,数据库从MySQL 5.7升到8.0,应用一启动直接抛ClassNotFoundException: com.mysql.jdbc.Driver。我上去一看,pom里的驱动还是mysql-connector-java 5.1.x,依赖坐标老、驱动类名也老,整套连接配置还停留在五年前的写法。这种场面在8.0普及之后真的不少见。MySQL 8.0版本的JDBC驱动Jar包,表面上只是换一个jar的事,实际牵涉到坐标变更、驱动类名替换、连接参数重配、认证插件兼容等一堆细节。这篇文章会把下载、版本选择、项目接入、排障经验完整过一遍,正在升级数据库的可以直接照着改,新建项目也可以少走弯路。
1. 不是单纯换个Jar包:8.0驱动到底变了什么
很多人觉得驱动嘛,从官网下载一个新版本替换掉旧的就行。真这么干,十有八九会在启动阶段被各种异常教做人。MySQL 8.0发布后,JDBC驱动在很多层面都做了调整,不是简单升个版本号。
1.1 包名和坐标都改了,别再抄旧教程
先说最直观的变化。MySQL 8.0之前,官方JDBC驱动的Maven坐标一直是com.mysql:mysql-connector-java,artifactId里有java这个词。但进入8.0时代后,官方把GAV坐标改成了com.mysql:mysql-connector-j,从8.0.31版本开始全面切换。也就是说,你现在去Maven Central搜,同一个驱动存在两种坐标,旧坐标停更,新坐标才是官方持续维护的版本。
我见过不少项目还在pom里写:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.26</version> </dependency>这个写法本身能跑,但它引的是旧坐标的过渡版本。新项目建议直接用官方新坐标,老项目等数据库升级时顺势一起改。
| 阶段 | GAV坐标 | 说明 |
|---|---|---|
| 8.0早期 | com.mysql:mysql-connector-java | 过渡期坐标,网上教程大量引用 |
| 8.0.31起 | com.mysql:mysql-connector-j | 官方新坐标,长期维护 |
| 5.x/旧版 | mysql:mysql-connector-java | 远古坐标,不建议再使用 |
1.2 驱动版本和数据库版本不是一一对应的
很多刚接触8.0的开发者会问一个问题:驱动版本是不是必须和数据库版本完全一致?比如数据库是8.0.33,驱动也必须用8.0.33?答案是否定的。JDBC驱动是向下兼容的,mysql-connector-j 8.0.x系列可以连接MySQL 5.7、5.6这些老版本,所以驱动版本选型时,核心看的是你的Java环境和需要的新特性,而不是死磕与数据库版本相同。
真正需要注意的是下限。MySQL 8.0的默认认证插件换成了caching_sha2_password,这个插件在5.1.7之后才出现,在8.0.9之后成为默认。如果你的驱动还停留在5.x,连MySQL 8.0时大概率会报Unable to load authentication plugin 'caching_sha2_password'。所以连接8.0数据库,驱动版本至少得从8.0.x起步。我个人的习惯是,只要不是项目有强制约束,一律用当前最新的稳定版驱动。驱动这玩意儿,新版本修复的bug和兼容性问题往往比新功能更有价值。
1.3 先确认你的Java环境再选版本
驱动版本还受Java版本影响。mysql-connector-j 8.0.x要求Java 8及以上,如果你的项目还在Java 7,那只能停留在旧架构的5.1.x版本,但同时也就失去了连接MySQL 8.0默认认证插件的能力。从8.1开始,官方对Java版本的要求进一步提升,所以选型之前,先确认JDK版本再锁驱动版本,顺序不能反。
2. Jar包获取的几条正经路子
既然要接入驱动,第一步自然是把Jar包拿到手。这里我给三条路子,各有利弊,按场景选就行。
2.1 官网下载和Maven Central获取
最直接的途径是去MySQL官网的下载页面找Connector/J,选Platform Independent的版本。下载下来的是zip压缩包,解压之后能看到mysql-connector-j-8.x.x.jar和源码包。这个路子适合不熟悉Maven的初学者,或者项目本身是传统Web应用,需要手动把jar放到WEB-INF/lib里。
不过我更推荐直接使用Maven Central。打开中央仓库网站搜索mysql-connector-j,能找到所有历史版本,每个版本的.jar文件、.pom文件、校验和都有,比官网下载还方便。这里有一个细节很多人忽略:下载完jar后,最好核对一下SHA-1或MD5校验值,确保你拿到的包没有被篡改,尤其是从非官方渠道下载的情况下。
2.2 Maven项目引入的推荐写法
Maven项目建议直接引入新坐标,并加上runtime或者省略scope。JDBC驱动在编译期一般不直接引用类,只是在运行期需要加载,所以scope通常可以写成runtime:
<dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.0.33</version> <scope>runtime</scope> </dependency>Gradle对应写法:
implementation 'com.mysql:mysql-connector-j:8.0.33'scope为什么建议用runtime而不是默认的compile?因为驱动类在编译期用不到,用runtime可以减少编译期依赖干扰,也能避免一些打包插件把不必要的依赖放进产物里。这个建议主要针对规范项目,如果是自己维护的小项目,用默认compile也没问题。
版本号这里,8.0.33是我验证过的稳定版本,你写的时候可以替换成当时的官方最新版。驱动别一直拖着一个老版本不放,至少每年升一次小版本,能省掉很多不明原因的连接问题。
2.3 手动导入IDE和本地仓库的坑
部分老项目不用依赖管理,或者说用的是Eclipse、IDEA直接手动导入jar。IDEA里的操作路径:菜单File -> Project Structure -> Modules -> Dependencies -> 点加号 -> JARs or directories,选中下载的驱动包加入Libraries。这一步本身不难,难的是后来换机器或者换人接手时,jar经常被遗忘。这种手工维护方式隐患很大,我的建议是,哪怕项目再小,也尽量引入Maven或Gradle,把依赖统一管起来。
如果项目必须用手动jar,但又想保留依赖管理的便利,可以先把驱动jar安装到本地仓库:
mvn install:install-file -Dfile=mysql-connector-j-8.0.33.jar -DgroupId=com.mysql -DartifactId=mysql-connector-j -Dversion=8.0.33 -Dpackaging=jar这条命令的坑在于,如果你本地的驱动jar和服务器实际部署时的驱动jar来源不一致,很容易出现开发环境跑得好好的,一到测试环境就报No suitable driver found。后面我会专门说这个场景的排查思路。
3. 接入8.0驱动必须改的代码和参数
Jar包拿到手只是开始。代码里那些和驱动相关的配置才是真正决定能不能连上数据库的关键,尤其是从老版本升级上来的项目,每一处都值得核对。
3.1 驱动类名变了,旧代码直接罢工
MySQL 8.0驱动最直观的代码改动就是驱动类名。旧版本用com.mysql.jdbc.Driver,8.0之后推荐使用com.mysql.cj.jdbc.Driver,并且旧类在8.0驱动中已经被移除。如果你在代码里写:
Class.forName("com.mysql.jdbc.Driver");换成8.0驱动后,100%会抛ClassNotFoundException。需要改成:
Class.forName("com.mysql.cj.jdbc.Driver");这里顺便澄清一个常见误区:JDBC 4.0及以上规范支持SPI机制,也就是驱动包自带META-INF/services/java.sql.Driver配置,只要jar在类路径下,DriverManager会自动加载驱动,普通场景根本不需要写Class.forName。真正需要显式注册的场景很少,比如运行在特殊的类加载器环境里。老代码里的Class.forName加着也不是不行,换成新类名就能继续用。
3.2 连接URL里的时区、SSL和公钥参数
驱动类名替换之后,连接URL是第二个必改项。MySQL 8.0默认开启SSL和caching_sha2_password认证,驱动和服务器之间握手时多了一堆额外校验。如果URL还是最朴素的jdbc:mysql://localhost:3306/yourdb,大概率会踩时区或者SSL相关的坑。
推荐的最小可用URL长这样:
jdbc:mysql://localhost:3306/yourdb?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&characterEncoding=utf8逐个说这些参数的意义。
serverTimezone=Asia/Shanghai是经常被忽略但报错率极高的参数。MySQL服务器默认时区如果没设,驱动读取时容易报The server time zone value is unrecognized or represents more than one time zone,指定亚洲上海时区可以绕开这个检测。开发环境连本地数据库时,把URL里加上这个参数是最稳的。
useSSL=false代表开发环境关闭SSL加密。MySQL 8.0默认开启SSL,驱动默认也倾向于验证证书,一旦证书不可信会警告甚至拒绝连接。开发环境图省事可以关掉,测试和生产环境建议反过来开启,并用规范证书配置。
allowPublicKeyRetrieval=true是配合认证插件用的。MySQL 8.0默认caching_sha2_password,客户端第一次连接时需要通过RSA公钥交换密码。如果这个参数不允许,且又没有SSL保护,驱动会直接报Public Key Retrieval is not allowed。开发调试时打开它是最快的解法,生产环境还是建议用SSL,而不是一直开着公钥自动获取。
最后是characterEncoding=utf8,这个不用多解释,统一字符集,避免乱码。
3.3 连接池和ORM框架里的配置参考
现在项目基本都走连接池,HikariCP和Druid最常见。连接池配置里的jdbcUrl、driverClassName、username、password每一项都要对应更新。以HikariCP为例:
spring: datasource: hikari: jdbc-url: jdbc:mysql://localhost:3306/yourdb?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&characterEncoding=utf8 driver-class-name: com.mysql.cj.jdbc.Driver username: root password: yourpassword需要注意的是,jdbc-url和Spring Boot早期版本中的url是同一个东西,写法不同而已。如果用的Druid,driverClassName同样要写com.mysql.cj.jdbc.Driver,别写成旧类名。
我接手过一个项目,连接池滚动日志里全是SSL警告,后台人员怎么查都找不到原因,其实就是URL少了一个useSSL=false。这种问题不会让程序挂掉,但会刷爆日志,让真正的异常信息被淹没,排查起来极其痛苦。
4. 实际踩过的坑:常见报错与排查
驱动接入本身不复杂,但报错千奇百怪。这一节我挑几个真实高频的场景讲透。
4.1 高频报错一览与快速定位表
| 报错信息 | 常见原因 | 快速处理 |
|---|---|---|
| ClassNotFoundException: com.mysql.jdbc.Driver | 驱动类名未更新或jar缺失 | 换成com.mysql.cj.jdbc.Driver,确认jar在类路径下 |
| Unable to load authentication plugin 'caching_sha2_password' | 驱动版本过旧 | 升级到8.0.x驱动 |
| Public Key Retrieval is not allowed | 认证公钥获取受限 | URL加allowPublicKeyRetrieval=true或启用SSL |
| The server time zone value ... unrecognized | 服务器/客户端时区不一致 | URL加serverTimezone=Asia/Shanghai |
| No suitable driver found | jar缺失或URL格式有误 | 检查jar路径、classloader,确认URL前缀jdbc:mysql |
这张表覆盖了我遇到过的八成驱动问题。剩下两成基本是jar冲突,在分包部署时比较常见。
4.2 案例实录:ClassNotFound与部署包缺jar
一个典型场景:应用在IDEA里跑得好好的,打成war包扔到服务器就报ClassNotFoundException: com.mysql.cj.jdbc.Driver。遇到这种情况,第一反应不要去看代码逻辑,直接解压war包,检查WEB-INF/lib下有没有驱动jar。很多时候,开发环境依赖在IDE里是配好的,但打war包时没有把依赖带进去。
解决办法分两类。如果是Maven项目,确认依赖的scope没写provided(这个scope会阻止打包),改回runtime或compile后重新打包;如果用的是Eclipse或IDEA自带的编译打包插件,也得检查Artifact里是否包含依赖,打进去才行。另有一个隐蔽原因,是Tomcat等容器自带了一份老版本驱动,应用包里又打包了一份新驱动,类加载顺序导致使用了老的驱动类。这种情况优先看容器lib目录,和运维确认有没有重复放jar。
4.3 案例实录:Public Key Retrieval is not allowed 背后的逻辑
这个报错出现频率极高,我单独拿出来讲。MySQL 8.0默认的caching_sha2_password认证过程是:客户端向服务器发送密码前,需要先获取服务器的RSA公钥来加密密码。驱动出于安全考虑,默认不允许自动获取服务器公钥,除非连接建立了SSL加密通道。于是很多开发者在没有任何参数的情况下连接8.0,直接碰到这个错误。
理解了原理就能明白,allowPublicKeyRetrieval=true不是万能钥匙。它只是在没有SSL的环境中,允许客户端主动向服务器要公钥。代价是,如果网络被监听,理论上存在中间人风险,所以生产环境更推荐走SSL。开发环境用它省事,生产环境要严谨。
还有一个连带问题:即使加了allowPublicKeyRetrieval=true,如果服务器时区不对,URL里没有serverTimezone,它会先报时区错误。所以遇到这类连接报错时,按URL参数逐一补齐是最稳妥的排查路径。
4.4 两种常见但容易忽略的jar冲突场景
Maven项目中,依赖树里同时出现多个驱动版本,是典型的jar冲突来源。执行mvn dependency:tree看输出,如果发现mysql-connector-java和mysql-connector-j同时存在,一定要处理。这两个坐标本质是同一个驱动,同时存在会导致类路径里出现相同类名的不同版本,有些类加载器会随机加载其中一个,尤其是通过不同间接依赖引入时,极易选中老版本。
处理办法是用exclusion把旧坐标排除掉:
<dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.0.33</version> </dependency> <dependency> <!-- 某个老库间接引入的驱动 --> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> <scope>provided</scope> </dependency>更彻底的方式是在全局依赖管理中显式锁定新版本,让子模块无论从哪里引入都统一到8.0.x。
另一个容易忽略的是容器环境。有些应用服务器自带一套数据库驱动。假如自带5.1版本的驱动,你的应用又打包进一个8.0驱动,应用启动时会优先加载谁?取决于类加载顺序,一般容器lib目录优先。这种问题很难在本地复现,只有部署到容器环境才暴露。排查思路是在启动脚本中加上类加载日志,或者干脆清掉容器lib下的旧驱动jar,保证全链路只用新驱动。
5. 一段可以直接参考的完整接入示例
每次讲完知识点,我都会习惯给一套可以直接落地的完整示例。这里给一个最常规的Spring Boot项目接入MySQL 8.0驱动的案例。
5.1 依赖和配置文件
pom.xml依赖:
<dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.0.33</version> <scope>runtime</scope> </dependency>application.yml数据源配置:
spring: datasource: url: jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&characterEncoding=utf8 username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver这段配置在Spring Boot 2.x和3.x中都能使用。Spring Boot 3.x要求Java 17起步,驱动版本8.0.33完全兼容。
5.2 验证连接是否配置成功
配置完成后,怎么判断驱动真的生效?最直接的方式是启动应用后调用任意涉及数据库的接口。但我建议先做一次不依赖业务表的验证,比如写一个最简单的测试:
@SpringBootTest class DbConnectionTests { @Autowired private DataSource dataSource; @Test void testConnection() throws SQLException { try (Connection connection = dataSource.getConnection()) { System.out.println("connected: " + connection.getMetaData().getDriverName()); } } }这个测试能打印出当前实际使用的驱动名称。如果输出的是MySQL Connector/J,恭喜你,驱动接入成功。如果这里就报错,不用跑业务接口,问题范围可以立刻锁死在驱动加载和URL配置上。
我习惯在每次数据库或驱动升级后,把这个测试用例保留下来作为冒烟测试的一部分。成本低,一次运行几秒钟,却能在后续依赖调整时第一时间发现问题。
5.3 老项目升级时的一键检查清单
如果是老项目升级MySQL 8.0,我整理了一份自查清单,按顺序过一遍,基本不会出大错:
- 确认依赖坐标已经换成
com.mysql:mysql-connector-j,且全局只保留一个驱动版本; - 确认
com.mysql.jdbc.Driver这个类名在代码、配置文件、XML、YAML所有位置都已替换为com.mysql.cj.jdbc.Driver; - 确认所有
jdbc:mysql://开头的URL都补齐了useSSL、serverTimezone、characterEncoding参数; - 如果应用部署在自带驱动的应用服务器里,检查lib目录是否有旧驱动;
- 启动前跑一个数据源连通性测试,确认驱动名称和数据库版本都符合预期。
这套流程我每次升级都会走一遍,耗时不到半小时,省下的排查时间完全是翻倍的。
写在最后的几句实话
驱动这东西,平时没人注意,出问题就是连锁反应。我这两年经手的项目中,因为驱动版本和参数配置踩坑的,占了数据库相关问题的一多半。MySQL 8.0的JDBC驱动Jar包本身只是个载体,真正决定能不能顺利连接的是版本选型、类名替换和URL参数这三件事。新手最容易忽略的,是旧坐标和新坐标的差异,以及认证插件对驱动版本的最低要求。按这篇文章的顺序来,基本能顺畅接完。以后每次升级驱动,别再只替换jar完事,顺手把变更日志看一遍,很多坑其实官方文档里早就写了。