尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

JDBC连接MySQL实战:驱动jar包下载、引入与常见异常排查

JDBC连接MySQL实战:驱动jar包下载、引入与常见异常排查 简介一套面向Java开发者的JDBC连接MySQL数据库驱动资源包覆盖MySQL 5.7与8.0两个常用版本。压缩包共计635个文件大小8.15MB内容以Java源码、mysql-connector-java的jar包为主同时包含properties配置文件、HTML使用说明、XML配置及证书密钥文件既有开箱即用的驱动也有便于深入学习的源码与文档。资源从JDBC基础原理讲起梳理了加载驱动、建立连接、执行SQL、处理结果集、关闭连接的标准流程并给出可运行的代码示例帮助开发者快速打通Java与MySQL的交互链路。目前已有1030人学习并下载使用。除了基础连接资源还系统介绍了PreparedStatement防注入、连接池如C3P0、HikariCP应用、批处理优化、事务的提交回滚以及SQLException异常处理思路并针对MySQL 5.7与8.0的驱动兼容性进行了说明适合在Spring等框架中整合数据访问或手写JDBC进行数据库操作的开发者排查问题、优化性能是一份兼具上手价值与进阶参考的实用资源包。 做Java开发的人几乎都绕不过数据库连接这一关而JDBC连接MySQL又是其中最常见也最容易踩坑的一环。很多新手一开始都会卡在“jar包”上项目一跑就报类找不到或者驱动加载不出来翻来覆去其实多半就是jar包没用对。这篇文章我就围绕“JDBC连接MySQL数据库jar包”这个话题把驱动包的下载、引入、代码编写到常见错误排查完整过一遍适合刚入门Java数据库编程的同学也适合已经把代码跑起来但偶尔被奇奇怪怪异常折磨的开发者参考。1. 先说结论JDBC和驱动jar包到底是怎么回事1.1 JDBC并不是一个jar包而是一套接口规范很多初学者会把JDBC和MySQL驱动当成同一个东西其实这是两个层面。JDBC的全称是Java Database Connectivity它是Java官方定义的一套数据库访问接口本身只提供规则不负责具体实现。真正干活的是各个数据库厂商根据这套规则写出来的驱动包比如MySQL的驱动就是mysql-connector-java。用个生活化的类比JDBC是电源插座的标准MySQL驱动是符合这个标准的插头。你只要认准插座规格不管插头是哪个厂商产的都能通电。所以你在代码里写的Connection、Statement、ResultSet这些类全部来自JDK自带的java.sql包而真正和MySQL协议打交道、完成网络通信的是jar包里的com.mysql.cj.jdbc.Driver类。这个区分非常关键。很多人以为把jar包放进项目就万事大吉结果代码里继续用着错误的老驱动类名或者JDK版本和驱动版本不匹配一样白搭。理解JDBC是规范、jar包是实现的本质后面排查问题就有方向了。1.2 驱动jar包的版本选择为什么这么关键MySQL官方驱动迭代了很多年老一点的叫mysql-connector-java从8.0.31之后官方把坐标改成了mysql-connector-j。版本之间最直观的区别是驱动类名变了5.x系列驱动类是com.mysql.jdbc.Driver8.x系列驱动类是com.mysql.cj.jdbc.Driver如果你拿到一份网上复制的老代码直接用8.x驱动却还在加载com.mysql.jdbc.Driver程序照样能跑但控制台会刷警告提示你驱动类已过时。反过来说数据库是MySQL 8.0以上你还在用5.1.49这种老包连接时大概率遇到SSL握手失败或者认证插件不兼容的问题。版本选择的另一个重要维度是JDK版本。mysql-connector-java 5.1.x基于JDK 5编译8.0.x基于JDK 8编译如果你的项目用的是JDK 17甚至更高版本建议直接用当前最新的8.x系列驱动不支持更老版本。还有一点容易被忽略驱动包里会捆绑一些第三方依赖比如protobuf、slf4j-api使用Maven时这些依赖会透传进来和项目里已有依赖可能会产生冲突后面有专门的排查章节。2. 获取驱动jar包的几种靠谱方式2.1 Maven或Gradle依赖管理最省心的方案现在的Java项目绝大多数都用Maven或Gradle做依赖管理这也是我推荐的获取方式。拿Maven来说在pom.xml里加一段依赖就行。MySQL 8.0系列的最新驱动坐标如下dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId version8.3.0/version /dependency有些项目还在用老坐标dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency这两个坐标本质上对应同一个驱动只不过从8.0.31开始官方换了groupId和artifactId。如果你在中央仓库搜索mysql-connector-java8.0.31之后的版本就搜不到了只能搜mysql-connector-j这一点很容易踩坑。需要注意的是Maven会自动把驱动依赖的传递性依赖拉下来。如果项目里已经有老版本的protobuf、gson等库Maven的依赖仲裁机制通常会选路径更近或者版本更高的那个。如果仲裁结果不理想你得在pom.xml里用exclusion排除掉不需要的依赖。我曾经遇到过一个项目驱动自带的protobuf-java版本比较新和Hadoop生态里的老版本冲突导致序列化报错最后就是把驱动里的protobuf排除掉改用项目统一的版本解决的。Gradle用户对应的写法是implementation com.mysql:mysql-connector-j:8.3.02.2 手动下载jar包并导入项目如果你做的是非Maven项目或者公司内网不允许访问中央仓库那就只能手动下载jar包了。MySQL官方下载地址在dev.mysql.com/downloads/connector/j/进去之后选择Platform Independent下载zip包。注意不要下载msi或dmg格式那个是可视化安装程序对我们这种普通开发场景来说太重了。下载完解压里面会有一个mysql-connector-j-x.x.x.jar文件。以8.3.0为例解压后名字是mysql-connector-j-8.3.0.jar。IDE里按项目导入IDEA中File - Project Structure - Libraries - 加号 - Java - 选中jar包Eclipse中右键项目 - Build Path - Configure Build Path - Libraries - Add External JARs手动导入最痛的点是最后打包部署的时候容易漏掉。你在IDEA里本地跑没问题一打成war包或者jar包发布到服务器就报ClassNotFoundException。这个坑我踩过不止一次所以现在我用非Maven项目时都会在打包脚本里显式把jar包目录加进WEB-INF/lib或者classpath中。2.3 确认jar包真的加载成功无论用哪种方式引入最好都做一个快速验证。写一个最简单的类打印驱动版本public class DriverCheck { public static void main(String[] args) throws Exception { Class? clazz Class.forName(com.mysql.cj.jdbc.Driver); System.out.println(驱动类加载成功: clazz.getName()); } }如果你点跑没报ClassNotFoundException说明驱动jar包已经在classpath里了。这一步看起来简单但能帮你把“项目结构问题”和“代码逻辑问题”快速拆开。classpath环境变量、IDE的module设置、打包插件的resource配置任何环节出错都会在这里暴露。3. 核心操作从零写一个JDBC连接MySQL的例子3.1 驱动类名和URL参数的细节连接之前你需要确定三件套驱动类名、连接URL、用户名密码。以MySQL 8.0为例驱动类是com.mysql.cj.jdbc.DriverURL格式是jdbc:mysql://ip:port/databaseName?参数值1参数值2常见参数有这几个我逐个说明一下作用useSSLMySQL 8.0默认开启了SSL相关协商。本地开发连测试库一般不需要加密设成false能省去不少麻烦。如果不设很多版本会报SSL handshake的警告虽然不致命但很烦。serverTimezone这是时区参数。不设置的话驱动默认用JVM的时区去匹配数据库时的时区如果两边不一致读写DATETIME类型的数据就可能差8小时。建议直接写成Asia/Shanghai不管服务器部署在哪只要业务面向中国用户就这么写。allowPublicKeyRetrieval这个参数是在用caching_sha2_password认证插件时用的。MySQL 8.0默认用户认证插件是caching_sha2_password某些网络环境下第一次连接需要从服务器获取公钥如果不允许就会报错。本地开发建议设成true生产环境要评估风险后再决定。useUnicode和characterEncoding这两个参数配合使用保证中文不乱码推荐这么写useUnicodetruecharacterEncodingutf8。还有queryTimeout、rewriteBatchedStatements等参数分别在执行查询超时和批量插入场景下使用。我把常用参数整理成一张表方便复制参数示例值作用useSSLfalse是否启用SSL连接本地开发建议falseserverTimezoneAsia/Shanghai指定服务器时区避免日期时间偏差allowPublicKeyRetrievaltrue允许获取服务器公钥解决认证插件问题useUnicodetrue使用Unicode编码characterEncodingutf8指定字符集防止中文乱码rewriteBatchedStatementstrue优化批量插入性能queryTimeout10查询超时时间单位秒3.2 完整代码示例与执行流程下面是一个最基础的JDBC连接MySQL示例注释里标明了每一步的作用import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; public class JdbcDemo { public static void main(String[] args) { // 三个核心配置项 String url jdbc:mysql://localhost:3306/test_db ?useSSLfalseserverTimezoneAsia/Shanghai allowPublicKeyRetrievaltrue useUnicodetruecharacterEncodingutf8; String username root; String password your_password; Connection conn null; Statement stmt null; ResultSet rs null; try { // 1. 加载驱动 Class.forName(com.mysql.cj.jdbc.Driver); // 2. 获取连接 conn DriverManager.getConnection(url, username, password); // 3. 创建Statement对象 stmt conn.createStatement(); // 4. 执行SQL并获取结果集 rs stmt.executeQuery(SELECT id, username FROM user LIMIT 10); // 5. 遍历结果集 while (rs.next()) { System.out.println(id rs.getInt(id) , username rs.getString(username)); } } catch (Exception e) { e.printStackTrace(); } finally { // 6. 释放资源顺序是先ResultSet再Statement最后Connection if (rs ! null) { try { rs.close(); } catch (Exception e) { } } if (stmt ! null) { try { stmt.close(); } catch (Exception e) { } } if (conn ! null) { try { conn.close(); } catch (Exception e) { } } } } }这段代码虽然每个环节都写了但实际项目中我一般不会直接这样写而是用连接池HikariCP、Druid去管理Connection。原因很简单每次手动创建和关闭连接开销很大。连接池能复用连接类似线程池的意义。不过看连接池源码的时候你会发现底层还是DriverManager.getConnection这套逻辑所以把原生JDBC流程吃透是理解一切数据库访问框架的基础。3.3 为什么“加载驱动”这一步可以省略但我仍然建议写在JDBC 4.0之后驱动jar包里的META-INF/services/java.sql.Driver文件已经声明了驱动类所以DriverManager可以通过SPI机制自动加载驱动Class.forName这行不写也能跑。我看到很多教程已经不写这行代码了但我的习惯是保留原因有两个第一显示写出来能让代码的可读性更强新人看到这行就知道连接的是哪个数据库。第二如果你在类加载机制上做了特殊处理比如自定义ClassLoader隔离classpath自动加载可能会有意外问题手动加载更可控。当然有些场景下写Class.forName反而会重复加载。比如在Tomcat这种Web容器里驱动已经被全局加载过一次你在应用代码里又加载一次可能出现“两个Driver实例”的诡异问题。这种情况通常出现在把驱动jar包放进了容器的lib目录同时应用lib里又有同名jar包。解决办法是二选一别放在两个地方。4. 实操中的高频问题和排查技巧4.1 ClassNotFoundException和NoClassDefFoundError这两个异常名字很像但含义完全不同。ClassNotFoundException通常发生在类加载阶段意思是“在classpath里根本没找到这个类”比如你写Class.forName(com.mysql.cj.jdbc.Driver)时报这个错就是jar包没引入或者引入了但不在运行时classpath里。NoClassDefFoundError则是“之前这个类加载过但现在JVM找不到它的定义了”常见场景是编译时类存在、运行时jar包缺失。比如你用Maven编译依赖了驱动但打包时又把驱动排除掉了就会在运行阶段报这个错误。排查思路永远是先确认jar包到底在不在classpath路径上。在IDEA里可以打开Project Structure看dependencies或者用压缩工具直接打开最终打的jar包看看有没有com/mysql/cj/jdbc/Driver.class。这个文件在不在比看一万遍代码都管用。4.2 Communications link failure与时区、SSL问题Communications link failure是我见过最多的连接异常字面意思是“通信链路失败”。它的可能原因非常多包括MySQL服务没启动、端口不对、防火墙拦截、数据库连接数打满、网络不稳、连接超时等。但如果是本地开发刚配置完环境就报这个错大概率是这几个原因MySQL服务没起来或者监听的端口不是3306URL里的IP和端口写错MySQL服务监听的地址是127.0.0.1但你用localhost解析到IPv6地址去连。另外两种高频组合错误也和这个有关一种是SSL握手失败把useSSL参数设成false就能解决另一种是Public Key Retrieval is not allowed加上allowPublicKeyRetrievaltrue即可。我发现很多问题只要把URL参数写完整一次性就能少掉80%的古怪连接错误。建议参考3.1节的URL模板用一套标准的参数组合做基线再按需增删。4.3 jar包冲突与版本不一致jar包冲突在Maven项目里特别常见。一个中等规模的项目可能引入十几个模块每个模块又引入自己的依赖最后很可能出现多个版本的mysql驱动同时存在的情况。Maven的依赖仲裁机制不一定帮你选到想要的版本。判断冲突最好的工具是IDEA自带的依赖分析功能打开pom.xml右上角有Maven面板点“Show Dependencies”可以图形化查看依赖树。如果发现某个依赖下面传递进来了一个老版本驱动可以用exclusion排除dependency groupIdcom.example/groupId artifactIdsome-module/artifactId version1.0.0/version exclusions exclusion groupIdmysql/groupId artifactIdmysql-connector-java/artifactId /exclusion /exclusions /dependency还有一个很好的习惯是统一在父pom的dependencyManagement里锁定驱动版本所有子模块都不写版本号这样能最大程度避免纷乱的版本覆盖。我见过太多因为两个模块各自引了不同版本驱动导致一会儿能连一会儿不能连的诡异故障。4.4 批量插入和连接池场景下的jar包问题批量插入是JDBC的高频使用场景。MySQL本身支持rewriteBatchedStatements参数开启后驱动会把多条INSERT语句重写成一条多VALUES的语句提交性能能提升好几倍。这个参数是在URL里配置的和jar包版本也有关系。老版本驱动对大批量语句的重写支持有限如果发现批量插入性能没有预期提升先检查驱动版本是否过旧再看URL参数是不是写对了。连接池场景下要注意的坑是连接池初始化时机和驱动加载顺序。像HikariCP这种连接池默认在第一次getConnection时才初始化连接如果那时候驱动还没加载就会报Unable to obtain connection from database。解决办法是在项目启动阶段显式触发一次Class.forName或者直接在URL里去连一次数据库。另外连接池里连接的空闲回收和MySQL的wait_timeout参数要协调好。MySQL默认空闲8小时断开但连接池里的连接可能一直存在等到下次复用时就拿到一个已经失效的连接报错也是Communications link failure。HikariCP可以设置connectionTestQueryDruid可以通过testWhileIdle定期检测这些配置和驱动本身无关但排查问题时要一起考虑。5. 我个人的一些心得体会从第一次被ClassNotFoundException折磨到现在我对JDBC连接MySQL这件事最大的体会是99%的问题都出在“版本不一致”和“classpath不对”上而不是代码逻辑本身。所以每次接到别人报过来的连接问题我第一句话永远是问你的驱动版本是多少你用的是什么数据库版本而不是先看代码。关于驱动版本我的建议是尽量统一到8.x最新稳定版不要再纠结5.x老版本。虽然5.x在很多老项目里跑得挺好但新项目如果还选5.x等于提前给自己埋雷尤其是MySQL 8.0默认的认证插件是caching_sha2_password5.x驱动根本认不了这种认证方式必须手动改用户插件代价更大。还有一个小技巧想分享因为驱动配置经常要写URL、用户名、密码我习惯把这些参数全部提取到配置文件里用变量拼接。这样环境切换时只改配置文件就行不用动代码。最后再补充一点网上很多教程里的URL参数拷贝过来不一定适合你当前的MySQL版本最好的办法是打开MySQL官方文档的Connector/J章节确认一下自己用的版本支持哪些参数。数据库连接是每个Java应用的命脉多花十分钟把jar包和参数搞明白比出问题时熬夜排查要划算得多。本文还有配套的精品资源点击获取
返回列表