问题背景
最近在使用 JDBC 连接 MySQL 数据库时遇到了一个报错:com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException。经过一番排查,发现问题出在 MySQL 驱动版本和数据库版本不匹配上。
今天就把完整的排查过程和解决方案分享给大家,希望能帮助到遇到同样问题的朋友。
问题描述
我使用的代码如下:
public class Conn {
final String DB_URL = "jdbc:mysql://localhost:3306/message";
final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
final String USER = "root";
final String PASS = "password";
public Connection cn = null;
public PreparedStatement pr = null;
public ResultSet rs = null;
public Conn() {
try {
Class.forName(JDBC_DRIVER);
cn = DriverManager.getConnection(DB_URL, USER, PASS);
} catch (Exception e) {
e.printStackTrace();
}
}
public void close() {
try {
if (cn != null) cn.close();
if (pr != null) pr.close();
if (rs != null) rs.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行后出现了连接错误。
问题原因分析
经过检查后发现,我连接的 MySQL 数据库是 8.0 版本,而项目中使用的 com.mysql.jdbc.Driver 驱动包是 5.1 版本。
这就是问题所在:驱动版本太老了,不支持 MySQL 8.0 的新特性。
第一个错误:驱动类过时
于是我将 com.mysql.jdbc.Driver 的版本改为 8.0.11,重新执行代码后,出现了新的警告:
Loading class 'com.mysql.jdbc.Driver'. This is deprecated. The new driver class is 'com.mysql.cj.jdbc.Driver'.
The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
警告信息解读:
com.mysql.jdbc.Driver已经被弃用- 新的驱动类是
com.mysql.cj.jdbc.Driver - 驱动会通过 SPI 自动注册,通常不需要手动加载
第二个错误:SSL 连接警告
继续执行后,又出现了 SSL 相关的警告:
Thu May 28 10:34:56 CST 2020 WARN: Establishing SSL connection without server's identity verification is not recommended.
According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set.
For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'.
You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
警告信息解读:
- 不建议在没有服务器身份验证的情况下建立 SSL 连接
- 根据 MySQL 5.5.45+、5.6.26+ 和 5.7.6+ 的要求,默认情况下必须建立 SSL 连接
- 我们需要通过设置
useSSL=false显式地禁用 SSL - 或者设置
useSSL=true并为服务器证书验证提供信任存储
第三个错误:时区问题
修改后继续执行,又出现了一个新错误:
Exception in thread "main" java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone.
You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
错误信息解读:
- 这是一个时区问题
- 数据库安装时默认为英语,0:00 时区
- Windows 系统中,XP 的时区是 GMT,而 Win7 的时区是 UTC
- MySQL 返回的时间会比实际时间要早 8 小时
- 提示很明确:可以通过配置服务器或通过
serverTimeZone配置 JDBC driver 连接参数来指定一个特定的时区
解决方案
方案一:配置 JDBC 连接参数(推荐)
在 URL 连接字符串后面加上参数:?useSSL=false&serverTimezone=UTC
修改后的代码:
public class Conn {
final String DB_URL = "jdbc:mysql://localhost:3306/message?useSSL=false&serverTimezone=UTC";
final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";
final String USER = "root";
final String PASS = "password";
public Connection cn = null;
public PreparedStatement pr = null;
public ResultSet rs = null;
public Conn() {
try {
Class.forName(JDBC_DRIVER);
cn = DriverManager.getConnection(DB_URL, USER, PASS);
} catch (Exception e) {
e.printStackTrace();
}
}
public void close() {
try {
if (cn != null) cn.close();
if (pr != null) pr.close();
if (rs != null) rs.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
方案二:修改 MySQL 数据库配置
如果你不想每次配置 JDBC 参数都使用 serverTimezone=UTC 来进行连接,也可以直接修改 MySQL 数据库的时区配置。
查看当前时区设置:
show variables like '%time_zone%';
输出示例:
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| system_time_zone | |
| time_zone | SYSTEM |
+------------------+--------+
设置为东八区(北京时间):
set global time_zone='+8:00';
执行结果:
Query OK, 0 rows affected (0.00 sec)
验证修改:
重新打开命令行,进入 MySQL,查询时区:
show variables like "%time_zone%";
输出结果:
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| system_time_zone | |
| time_zone | +08:00 |
+------------------+--------+
2 rows in set, 1 warning (0.01 sec)
此时,即使 JDBC 不加 serverTimezone=UTC,仍然可以正确连接。
常用时区参数
在 JDBC URL 中,可以使用以下时区值:
| 时区值 | 说明 |
|---|---|
UTC |
协调世界时 |
GMT |
格林尼治标准时间 |
GMT%2B8:00 |
东八区(北京时间,URL 编码的 GMT+8:00) |
Asia/Shanghai |
上海时区 |
Asia/Hong_Kong |
香港时区 |
Asia/Taipei |
台北时区 |
推荐使用:
- 国际化应用:
serverTimezone=UTC - 国内应用:
serverTimezone=Asia/Shanghai或serverTimezone=GMT%2B8:00
常见问题
Q1:useSSL=true 和 useSSL=false 有什么区别?
- useSSL=true:使用加密连接,更安全,但需要配置证书
- useSSL=false:不使用加密连接,适合开发环境
Q2:为什么会出现乱码?
时区错误信息中出现的 'Öйú±ê׼ʱ¼ä' 是乱码,原文应该是"中国标准时间"。这是因为字符编码问题导致的。
解决方法:
- 在 JDBC URL 中添加字符编码参数:
?characterEncoding=utf8
完整的 URL 示例:
jdbc:mysql://localhost:3306/message?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
Q3:时区设置会影响什么?
时区设置主要影响:
TIMESTAMP类型的字段NOW()、CURRENT_TIMESTAMP等函数- 时间戳的读取和写入
希望这篇记录能帮助到遇到同样问题的朋友!如果你有其他问题或更好的解决方案,欢迎在评论区交流。
默认评论
Halo系统提供的评论