对比5.1.48VS8.0.16版本 getConnection、isValid
一、驱动以及目录结构
官网地址:https://dev.mysql.com/doc/relnotes/connector-j/8.0/en/
1.1 驱动对比
5.1.48 引入驱动时名称为:com.mysql.jdbc.Driver
8.0.16 引入驱动时名称为:com.mysql.cj.jdbc.Driver ,如果 驱动还配置 5.x 的版本,也没有问题,只是会如下提示:
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.
但是还是会自动 切换成 新的驱动,原因如下,主要是做了代码兼容,让原先的Driver继承了com.mysql.cj.jdbc.Driver ,然后在
然后com.mysql.cj.jdbc.driver 里面的 静态块 进行注册:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
//
// Register ourselves with the DriverManager
//
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
}
在DriverManager 类里面的 通过了SPI 进行了注册和初始化:
/**
* Load the initial JDBC drivers by checking the System property
* jdbc.properties and then use the {@code ServiceLoader} mechanism
*/
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
1.2 目录结构对比
5.1.X的源码目录结构和 8.0.16 的代码结构也有很大的变化,这里就不贴图了,感兴趣的自己去下载源码看,源码地址:gitHub地址
这里对比一下 对JDK 的要求:
此外,JDBC 版本和 JDK 对应的关系如下:
一、JDBC 1.0 随JDK1.1发布;
二、JDBC 2.0 随JDK1.2 和 JDK1.3 发布
JDBC 2.0 和 JDBC 2.1 API 被分入两个包:
java.sql 包(包括核心 API;它是 JDBC 1.22 API 的增强)
javax.sql 包(可选的包,用于支持连接池、分布式事务及其他类似的高级功能)。
三、JDBC3.0 随JDK1.4发布
四、JDBC 4.0 随 JDK1.6 发布
五、JDBC4.1 随 JDK1.7 发布
六、JDBC4.2 随 JDK1.8 发布
二 getConnection流程对比
这里主要是分析的正常流程:
2.1 5.1.48版本getConnection
大致流程:
- 判断url 是否为空,为空泽抛出异常
- 判断url 是否以"jdbc:mysql:loadbalance://" 开头,是则创建对应的负载均衡的connet,否则继续步骤3
- 判断url是否以“jdbc:mysql:replication://” 开头,是泽创建对应的Replication
的connect,否则继续 - 对URL 进行 解析,转换成Properties 以便后面使用
- 判断NUM_HOSTS 是否 >1 ,是 则 走connectFailover 流程,否则继续
- 判断是否是 jdbc4, 是支持jdbc4的,那就先 通过构造函数实例JDBC4Connection
- JDBC4Connection 继承了 ConnectionImpl,这里 会 createNewIO(false); 进行和mysql server进行创建connection
- 通过StandardSocketFactory 创建 socketFactory,使用标准的 TCP/IP sockets (the
standard) 进行socket 连接 - 通过BufferedOutputStream 进行 确定,这样一个connection就创建完成.
2.2、8.0.16版本的getConnection流程
大致流程如下:
- 判断url 是否为空,如果为空,抛出异常
- 通过正则表达式验证 URL的开头是否符合规定的,这里 通过枚举列出来,5.1.48 是逐个判断,个人感觉8.0.X的 代码结构相关整洁一些,类型如下,官方给出解释,不同的数据库用错驱动比较常见,比如用的ServerSQL 的数据库,用的mysql 的驱动
- 对URL 进行拼接,拼接玩,用此链接作为Key,从缓存里面获取对应的ConnectionUrl 实例
private static final LRUCache<String, ConnectionUrl> connectionUrlCache = new LRUCache<>(100);
4. 获取到了就直接返回,获取不到就 通过 Class.forName 进行构造
5. 拿到ConnectionUrl 之后,根据对应的Type 进行和 mysql Server 创建Connection
6. 先对各类参数进行初始化,然后 createNewIO(false);
7. 这里通过 TCP/IP sockets (the standard) 开始进行连接, 建立之后 ,连接的状态是
:unauthenticated user,还没去确认,等待client 端确认.
8. 这里相对 5.1.48 做了进一步的细化, 创建了一个 protocol , 明确了:物理连接仅负责 I / O流,而 protocol 负责创建 session ,以及 内部认证,protocol 对 读取和发送消息也进行了封装.,这里 protocol.connect 进行确认,正式创建连接
三、isValid 流程对比
两个版本的isValid ,都是拿到连接之后,通过socket 发送一个 a ping command给到 mysql Server ,然后读取对应的消息,如果获取到的是 java.io.EOFException ,说明此connection 已经断开,抛出异常, 两个版本的大致流程基本一样,这里就不贴对比了,直接贴一下两个的大致流程路径:
四、升级遇到的坑
4.1 SQL String cannot be empty
Error updating database. Cause: java.sql.SQLException: SQL String cannot be empty
这个是由于 原先的5.x 没有对 非空校验,只是校验了 是否为null,而8.x 多了一个校验.
4.2 Date 类型 问题
数据库里面是 Date(对应 java.sql.Date) 类型,查询的时候,传入的是 Date(java.util.Date) 类型,5.1.48 可以查询到,升级之后查询不到,主要原因是 8.X 的版本,在时间转换时加了 时区 进去,而5.x没有
解决办法: 服务器的 时区和 mysql服务器要一致
4.3 timestamp、datetime类型
在 5.1.48 的 版本里面 ,
如果 把表里的类型 为 timestamp,datetime 类型 直接转为 String 类型,后面有一个 .0 毫秒格式,如下:
2020-04-19 07:34:52.0
而在8.0.16 里面直接 转为:
2020-04-19 07:34:52
格式有细微的差别
五、对比总结
5.1 getConnection 小结
-
8.0.16 对整个代码的结构进行了调整, 原先 5.1.x 很多的if …else 这类分支 ,8.0.16版本 通过正则 匹配 过滤掉不符合 规范的url ,再 通过 枚举 方式 匹配对应的 情况,这样代码更加简洁, 也更加直接,不符合的url 直接返回而不需要到创建的时候才发现不符合,效率更高.
-
8.0.16 里面设置了一个 缓存,key 为拼接好的URL ,value 对对应的connectionUrl, 创建连接时,每次的url一样就不用每次都通过构造函数来进行创建 ,这里进行了一定的优化,创建的速度更快
-
在 和mysql server 进行创建 连接时,都是创建socket 连接,但8.0.16 把很多的代码进行了重新整合,引入了一个 protocol 辅助类,对一些读写 方法 都重新封装,层次更加清晰了一些,也对校验进行了一定程度的增强.
5.2 其他对比
-
8.0.16 依赖 JDBC 4.2 ,所以要求JDK1.8
-
8.0.16 版本修复了一些bug 和对一些方法进行了整合,详见:mysql-connector-java 官方描述