我们提供安全,免费的手游软件下载!

安卓手机游戏下载_安卓手机软件下载_安卓手机应用免费下载-先锋下载

当前位置: 主页 > 软件教程 > 软件教程

当 mysql-connector-java-5 遇上 MySQL8,终究还是错付了 → 门当户对真的很重要!

来源:网络 更新时间:2024-04-18 09:32:18

开心一刻

今天,老婆给我发消息

老婆:老公,儿子从隔壁邻居家回来了

老婆:是先打还是先洗?

我:先洗吧,万一打错人了呢

老婆:先洗脸吧,没错就边打边洗

起因

在我们的固有认知中, mysql-connector-java-5.x.x 连接的是 MySQL5 ,而 mysql-connector-java-8.x.x 连接的是 MySQL8

如果用 mysql-connector-java-5.x.x 连接 MySQL8 ,或者用 mysql-connector-java-8.x.x 连接 MySQL5 ,会出问题

至于出什么问题,呃...,我没想过,也没试过,我相信你们也是一样的

但无意间,我尝试了下,确实有问题

一开始,我的一个 demo 是基于 MySQL 5.7.36 mysql-connector-java 5.1.26 MyBatis-Plus 3.1.0

示例代码: play_it_safe

因为 MySQL 5.7.36 被我删了,而本机( windows 10 )上正好有 MySQL 8.0.26

那就用 MySQL 8.0.26 来跑 demo 呗,只修改数据库连接的 url username password

然后就出现了一系列的异常

java.math.BigInteger cannot be cast to java.lang.Long

启动直接异常

这确实出乎了我的意料,我认为的是:启动不会异常,运行中出现异常

既然异常来了,那就好好看看呗

怎么看?直接从异常堆栈中看,找到 Caused by

直接点击框住的,来到 mysql-connector-java 5.1.26 的源码

这个 for 循环遍历的是 sortedCollationMap indexEntry 则表示遍历的当前元素

我问个问题: java.math.BigInteger cannot be cast to java.lang.Long 异常是谁抛出来的, getKey() 还是 intValue()

很简单,用排除法嘛

Long 是有 intValue() 方法的,只会 NPE ,不会 java.math.BigInteger cannot be cast to java.lang.Long

那就是 getKey() 抛出的异常呗,我们猜想下什么情况会抛出这个异常

indexEntry 声明的类型虽然是 Map.Entry ,但实际它的 key 不是 Long 类型,而是 BigInteger 类型?

再往上追溯一下, sortedCollationMap 声明的类型是 TreeMap ,但实际存放元素的 key BigInteger 类型?

此时你们对这个猜想肯定有疑问:这编译不会报错吗

直接 put 是会编译报错的,但是如果我变通下呢

编译没问题,但是执行会有如下异常

同样也是 int collationIndex = entry.getKey().intValue(); 抛出的异常

但两者原因是否一致,我们还需要对 play_it_safe 验证

如何验证, debug 调试呗

断点不能打在 connectionimpl.java:1019 这一行,因为此时是遍历 sortedCollationMap ,而非往里面 put 元素

我们需要看,往 sortedCollationMap 里面 put key 具体是什么类型,所以断点打在 1002

会走 if 分支

F7 跟进去

key 的类型就是 BigInteger ,证明我们的猜想是对的!

但是大家别高兴的太早,根因还没找到,我们得继续跟进 com.mysql.jdbc.ResultSetImpl#getObject( int )

重点看这个 switch

继续跟进,你会发现来到 BIGINT 分支

注意,重点来了

这段代码很简单,相信大家都能看到,但我还是分 3 块给大家说明下

1、 unsigned 表示无符号,然后再取反则表示有符号,也就是说如果数据库字段类型是 有符号 bigint ,则用 java.lang.Long 接收字段值并返回

2、如果数据库字段类型是 无符号 bigint ,则用 java.lang.String 来接收字段值,如果值为 null 则直接返回 null

3、如果数据库字段类型是 无符号 bigint ,并且字段值不是 null ,则用 java.math.BigInteger 来接收字段值并返回

此刻相信大家对异常的根因有点明白了,但又没有完全明白,为什么了?

因为没有弄清楚来龙去脉,我给大家捋一下:

1、程序启动过程(初始化)中,mysql-connector-java 5.1.26 会构建字段级字符集/字符序到java字符编码名称的映射

也就是会执行 com.mysql.jdbc.ConnectionImpl#buildCollationMapping 方法

2、 buildCollationMapping 方法中会执行 SQL SHOW COLLATION 来获取 MySQL 的字符集和字符序

Id:字符序id

Charset:字符集名称

关于 MySQL 的字符集和字符序的更多信息,可查看 记一次字符串末尾空白丢失的排查 → MySQL 是会玩的!

3、从查询结果集中,逐行取第 3 列( Id )作为 key ,取第 2 列( Charset )作为 value put sortedCollationMap

对应代码就是: Util.resultSetToMap(sortedCollationMap, results, 3, 2);

4、在取 Id 列值的时候,会根据数据库列类型,用不同的 java 类型来接收列值

MySQL 8.0.26 中, COLLATIONS 表的 ID 列的类型是 bigint unsigned

那么用什么 java 类型来接收?往上面翻一翻,是不是用 java.math.BigInteger 来接收?

MySQL 5.7.36 中, COLLATIONS 表的 ID 列的类型却是 bigint(11)

所以用 java.lang.Long 来接收

5、 for 循环遍历 sortedCollationMap ,完成数据库字段级字符集/字符序到java字符编码名称的映射

indexEntry.getKey() MySQL 8.0.26 会抛出异常: java.math.BigInteger cannot be cast to java.lang.Long

而在 MySQL 5.7.36 则正常,不抛出异常

至此,大家对根因是不是完全明白了?

说的简单点就是: mysql-connector-java 5.1.26 配不上 MySQL 8

说的详细点就是: mysql-connector-java 5.1.26 适配的是 COLLATIONS bigint 类型的 ID ,而非 bigint unsigned 类型的 ID

说的抽象点就是:上岸第一剑 先斩意中人, MySQL 从 5 上岸到了 8, mysql-connector-java 如果不跟着上岸的话,会被第一个斩杀

Unknown system variable 'query_cache_size'

因为用的是 都说了能不动就别动,非要去调整,出生产事故了吧 中的 demo : play_it_safe

所以我顺手将 mysql-connector-java 调整到了 5.1.37 MySQL 还是 8.0.26 ,结果启动出现了另一个异常

看到这个异常,不知道你们第一反应想到的是什么?

我第一反应想到的是 MySQL 8 取消了查询缓存,而 MySQL 5 是有查询缓存的

异常提示的已经很明显了,我们来验证下

MySQL 5.7.36 是有系统变量 query_cache_size

MySQL 8.0.26 是没有的

后面我又将 mysql-connector-java 升级到 5.1.42 ,启动还是异常: Unknown system variable 'query_cache_size'

Could not create connection to database server

因为需要用 MySQL 5.7.36 做对比,所以还是 Docker 快速安装了一个 MySQL 5.7.36

顺带也安装了 MySQL 8.0.26 (前面用的是 Win10 版,这个相当于是 Linux 版本)

然后我又鬼使神差的将连接调整到了 Linux 版的 MySQL 8.0.26

mysql-connector-java 5.1.42 时,启动异常是: Unknown system variable 'query_cache_size'

mysql-connector-java 5.1.37 时,启动异常是: Could not create connection to database server

关键异常堆栈是:

真正的异常其实是 NPE ,大家可以参考前面讲到的方法去分析

mysql-connector-java 5.1.26 时,启动异常也是: Could not create connection to database server

关键异常堆栈是:

总结

MySQL 驱动版本和 MySQL 版本一定要门当户对,否则就会错付

要升就一起升,只升一边,就会出现升的一方看不上没升的那方:上岸第一剑 先斩意中人