druid连接池TestOnBorrow=true导致的程序启动失败

发布时间 2023-09-18 14:52:30作者: tomoto

现象:修改druid配置,启动application类,程序无法启动一直加载中,无法注册到Eureka。
mysql版本:8.0.15
配置:

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

依赖:

com.alibaba:druid:1.1.10
compile files('lib/mysql-connector-java-8.0.15.jar')
runtime('mysql:mysql-connector-java')

最终问题入口:

com.alibaba.druid.util.MySqlUtils#getLastPacketReceivedTimeMs

正式开始调试,第一步代码 setTestOnBorrow(true)

    @Bean
    @Primary
    @Qualifier("dataSource")
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(env.getProperty("spring.datasource.url"));
        dataSource.setUsername(env.getProperty("spring.datasource.username"));// 用户名
        dataSource.setPassword(env.getProperty("spring.datasource.password"));// 密码
        dataSource.setInitialSize(1);
        dataSource.setMaxActive(20);
        dataSource.setMinIdle(5);
        dataSource.setMaxWait(30000);
        dataSource.setValidationQuery("select user()");
        dataSource.setTestOnBorrow(true);
        dataSource.setTestWhileIdle(true);
        dataSource.setPoolPreparedStatements(false);
        return dataSource;
    }

入口:

com.alibaba.druid.pool.DruidDataSource#getConnection(long)

    public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
        init();

        if (filters.size() > 0) {
            FilterChainImpl filterChain = new FilterChainImpl(this);
            return filterChain.dataSource_connect(this, maxWaitMillis);
        } else {
            return getConnectionDirect(maxWaitMillis);
        }
    }

代码入口getConnectionDirect(maxWaitMillis)
跟进去这段一段代码 testConnectionInternal(poolableConnection.holder, poolableConnection.conn)

 if (testOnBorrow) {
     boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
     if (!validate) {
         if (LOG.isDebugEnabled()) {
             LOG.debug("skip not validate connection.");
         }

         discardConnection(poolableConnection.holder);
         continue;
     }
 }

继续找下去 MySqlUtils.getLastPacketReceivedTimeMs(conn)

if (valid && isMySql) {
    long lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(conn);
    if (lastPacketReceivedTimeMs > 0) {
        long mysqlIdleMillis = currentTimeMillis - lastPacketReceivedTimeMs;
        if (lastPacketReceivedTimeMs > 0 ... ...
    }
}

问题就出现在这:Utils.loadClass("com.mysql.jdbc.MySQLConnection");

if (class_connectionImpl == null && !class_connectionImpl_Error) {
    try {
        class_connectionImpl = Utils.loadClass("com.mysql.jdbc.MySQLConnection");
    } catch (Throwable error) {
        class_connectionImpl_Error = true;
    }
}

也就是说当前1.1.10不支持新的数据库驱动,
导致下面的代码Object connImpl = conn.unwrap(class_connectionImpl);由于驱动版本不一致解包失败
报错:"java.sql.SQLException: Unable to unwrap to interface com.mysal.jdbc.MySQLConnection'
升级druid到1.1.24后发现代码还是报错
此时1.1.24源码已经更新为:


        if (class_connectionImpl == null && !class_connectionImpl_Error) {
            try {
                class_connectionImpl = Utils.loadClass("com.mysql.jdbc.MySQLConnection");
                if (class_connectionImpl == null) {
                    class_connectionImpl = Utils.loadClass("com.mysql.cj.MysqlConnection");
                    if (class_connectionImpl != null) {
                        mysqlJdbcVersion6 = true;
                    }
                }
            } catch (Throwable error) {
                class_connectionImpl_Error = true;
            }
        }

调试发现class_connectionImpl = Utils.loadClass("com.mysql.jdbc.MySQLConnection");不为空
排查依赖项发现:runtime('mysql:mysql-connector-java')
这个配置导致因为没有指定具体的版本号导致项目默认依赖了mysql-connector-java:5.1.40
注释后解决,代码走正常想要用的驱动:

class_connectionImpl = Utils.loadClass("com.mysql.cj.MysqlConnection");