问题起因

最近项目中需要对已提供数据库钥匙的数据源进行查询,考虑到各方数据库版本不一致,单一jdbc驱动恐无法满足需要,故产生单个项目连接多JDBC驱动版本的想法

知识储备

由于本篇文章涉及到ClassLoader的相关知识,建议未了解过的朋友通过如下博客自行了解
彻底理解ClassLoader

相关资料

实现此功能的过程中参考了如下博文:
动态加载jdbc驱动(可测试jdbc不同版本兼容性)
Java 自定义 ClassLoader 实现隔离运行不同版本jar包的方式

相关代码

实体类:

  • 数据库连接信息实体:
/*** 数据库连接信息* 重写了equals和hashCode* 主要用于后续依赖commons-pool2做外部数据源连接池*/
public class ConnectionInfoPO {/** 连接字符串 */private String url;/** 连接用户名 */private String username;/** 连接密码 */private String password;public ConnectionInfoPO(String url, String username, String password) {this.url = url;this.username = username;this.password = password;}public String getUrl() {return url;}public String getUsername() {return username;}public String getPassword() {return password;}@Overridepublic boolean equals(Object o) {if (this == o) { return true; }if (o == null || getClass() != o.getClass()) { return false; }ConnectionInfoPO that = (ConnectionInfoPO) o;return url.equals(that.url) &&username.equals(that.username) &&password.equals(that.password);}@Overridepublic int hashCode() {return Objects.hash(url, username, password);}}
  • 驱动jar及驱动类相关信息实体:
/*** 驱动相关信息*/
public class DriverInfoPO {/** jar文件所在目录的相对路径 - 相对于驱动包存放根路径 */private String dirRelativeDirPath;/** jar文件名称 */private String jarFileName;/** 驱动类名称 */private String driverClassName;/*** 未分目录存放驱动包的构造* @param jarFileName jar文件名称* @param driverClassName 驱动类名称*/public DriverInfoPO(String jarFileName, String driverClassName) {this("",jarFileName,driverClassName);}/*** 分目录存放驱动包的构造* @param dirRelativeDirPath jar文件所在目录的相对路径 - 相对于驱动包存放根路径* @param jarFileName jar文件名称* @param driverClassName 驱动类名称*/public DriverInfoPO(String dirRelativeDirPath, String jarFileName, String driverClassName) {this.dirRelativeDirPath = dirRelativeDirPath;this.jarFileName = jarFileName;this.driverClassName = driverClassName;}public String getDirRelativeDirPath() {return dirRelativeDirPath;}public String getJarFileName() {return jarFileName;}public String getDriverClassName() {return driverClassName;}}

工具类:

  • 数据库连接工具类:
/*** 数据库连接工具类* @author WangQ*/
public class ConnectionUtil {private static final String LOCK = new String("LOCK");/** 驱动包存放根目录绝对路径 */private static final String driverJarRootDirAbsolutePath = "C:\\Users\\W_WANGQIONG\\Desktop\\drivers";/** 驱动对应类加载器集合 key: 驱动包路径:驱动类名称 value: urlClassLoader实例 */private static Map<String, URLClassLoader> urlClassLoaderMap = new ConcurrentHashMap<>();/*** 创建数据库连接* @param driverInfoPO 驱动信息* @param connectionInfoPO 连接信息* @return 数据库连接* @throws Exception 获取DB连接失败*/public static Connection create(DriverInfoPO driverInfoPO, ConnectionInfoPO connectionInfoPO) throws Exception {String jarFilePath = driverJarRootDirAbsolutePath + File.separator + driverInfoPO.getDirRelativeDirPath() + File.separator + driverInfoPO.getJarFileName();String key = String.format("%s:%s",jarFilePath, driverInfoPO.getDriverClassName());URLClassLoader loader = null;synchronized (LOCK) {loader = urlClassLoaderMap.get(key);if(loader == null){URL url = new URL("jar:file:" + jarFilePath + "!/");loader = new URLClassLoader(new URL[]{url});urlClassLoaderMap.put(key,loader);}}// 记录原始ClassLoader并设置当前线程的ClassLoaderClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();Thread.currentThread().setContextClassLoader(loader);// 获取数据库连接Driver driver = (Driver) loader.loadClass(driverInfoPO.getDriverClassName()).newInstance();Properties connInfo = new Properties();connInfo.put("user", connectionInfoPO.getUsername());connInfo.put("password", connectionInfoPO.getPassword());Connection conn = driver.connect(connectionInfoPO.getUrl(), connInfo);// 还原原始ClassLoaderThread.currentThread().setContextClassLoader(oldClassLoader);return conn;}/*** 关闭数据库连接*/public static void close(Connection conn){try {if(conn != null){conn.close();}} catch (SQLException throwables) {// ignore}}}

测试:

相关环境版本:

  • jdk: 1.8.0_251
  • mysql: 8.0.21
  • jdbc驱动jar: v3.1.14、v5.0.8、v5.1.47、v8.0.16

单jdbc驱动测试:

通过替换当前项目的jar包,获取DB连接得出各版本驱动连接8.0.21的结果

public class SingleDriverTest {public static void main(String[] args) throws ClassNotFoundException, SQLException {Class.forName("com.mysql.jdbc.Driver");DriverManager.getConnection("jdbc:mysql://localhost:3306/demo","root","123456");/*v3.1.14:Exception in thread "main" java.sql.SQLException: Client does not support authentication protocol requested by server; consider upgrading MySQL clientat com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2975)at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:798)at com.mysql.jdbc.MysqlIO.secureAuth411(MysqlIO.java:3700)at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1203)at com.mysql.jdbc.Connection.createNewIO(Connection.java:2572)at com.mysql.jdbc.Connection.<init>(Connection.java:1485)at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:266)at java.sql.DriverManager.getConnection(DriverManager.java:664)at java.sql.DriverManager.getConnection(DriverManager.java:247)at pub.ikkyu.test.SingleDriverTest.main(SingleDriverTest.java:14)*//*v5.0.8:Exception in thread "main" com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException: Client does not support authentication protocol requested by server; consider upgrading MySQL clientat com.mysql.jdbc.SQLError.createSQLException(SQLError.java:921)at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2985)at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:885)at com.mysql.jdbc.MysqlIO.secureAuth411(MysqlIO.java:3421)at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1247)at com.mysql.jdbc.Connection.createNewIO(Connection.java:2775)at com.mysql.jdbc.Connection.<init>(Connection.java:1555)at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:285)at java.sql.DriverManager.getConnection(DriverManager.java:664)at java.sql.DriverManager.getConnection(DriverManager.java:247)at pub.ikkyu.test.SingleDriverTest.main(SingleDriverTest.java:14)*//*v5.1.47:Tue Sep 01 11:40:54 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.*//*v8.0.16Loading 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.*/}
}

多版本驱动测试:

public class MulitJdbcVersionTest {private static final Map<String,String> conns = new HashMap<String, String>();static {conns.put("C:\\work\\projects\\dspool\\lib\\mysql-connector-java-3.1.14.jar", "com.mysql.jdbc.Driver");conns.put("C:\\work\\projects\\dspool\\lib\\mysql-connector-java-5.0.8.jar", "com.mysql.jdbc.Driver");conns.put("C:\\work\\projects\\dspool\\lib\\mysql-connector-java-5.1.47.jar", "com.mysql.jdbc.Driver");conns.put("C:\\work\\projects\\dspool\\lib\\mysql-connector-java-8.0.16.jar", "com.mysql.jdbc.Driver");}public static void main(String[] args) {ConnectionInfoPO connectionInfoPO = new ConnectionInfoPO("jdbc:mysql://localhost:3306/amms","root","wq968187");DriverInfoPO driverInfoPO = new DriverInfoPO("mysql-connector-java-3.1.14.jar","com.mysql.jdbc.Driver");try {Connection conn = ConnectionUtil.create(driverInfoPO, connectionInfoPO);System.out.println("mysql-connector-java-3.1.14: "+conn);ConnectionUtil.close(conn);} catch (Exception e) {e.printStackTrace();}driverInfoPO = new DriverInfoPO("mysql-connector-java-5.0.8.jar","com.mysql.jdbc.Driver");try {Connection conn = ConnectionUtil.create(driverInfoPO, connectionInfoPO);System.out.println("mysql-connector-java-5.0.8: "+conn);ConnectionUtil.close(conn);} catch (Exception e) {e.printStackTrace();}driverInfoPO = new DriverInfoPO("mysql-connector-java-5.1.47.jar","com.mysql.jdbc.Driver");try {Connection conn = ConnectionUtil.create(driverInfoPO, connectionInfoPO);System.out.println("mysql-connector-java-5.1.47: "+conn);ConnectionUtil.close(conn);} catch (Exception e) {e.printStackTrace();}driverInfoPO = new DriverInfoPO("mysql-connector-java-8.0.16.jar","com.mysql.jdbc.Driver");try {Connection conn = ConnectionUtil.create(driverInfoPO, connectionInfoPO);System.out.println("mysql-connector-java-8.0.16: "+conn);ConnectionUtil.close(conn);} catch (Exception e) {e.printStackTrace();}}/** * java.sql.SQLException: Client does not support authentication protocol requested by server; consider upgrading MySQL client* at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2975)* at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:798)* at com.mysql.jdbc.MysqlIO.secureAuth411(MysqlIO.java:3700)* at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1203)* at com.mysql.jdbc.Connection.createNewIO(Connection.java:2572)* at com.mysql.jdbc.Connection.<init>(Connection.java:1485)* at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:266)* at pub.ikkyu.utils.ConnectionUtil.create(ConnectionUtil.java:57)* at pub.ikkyu.pool.DatabaseConnectionPooledObjectFactory.create(DatabaseConnectionPooledObjectFactory.java:28)* at pub.ikkyu.pool.DatabaseConnectionPooledObjectFactory.create(DatabaseConnectionPooledObjectFactory.java:15)* at org.apache.commons.pool2.BaseKeyedPooledObjectFactory.makeObject(BaseKeyedPooledObjectFactory.java:62)* at org.apache.commons.pool2.impl.GenericKeyedObjectPool.create(GenericKeyedObjectPool.java:1041)* at org.apache.commons.pool2.impl.GenericKeyedObjectPool.borrowObject(GenericKeyedObjectPool.java:357)* at org.apache.commons.pool2.impl.GenericKeyedObjectPool.borrowObject(GenericKeyedObjectPool.java:279)* at pub.ikkyu.test.DatabaseConnectionPoolTest.main(DatabaseConnectionPoolTest.java:41)
com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException: Client does not support authentication protocol requested by server; consider upgrading MySQL client* at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:921)* at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2985)* at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:885)* at com.mysql.jdbc.MysqlIO.secureAuth411(MysqlIO.java:3421)* at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1247)* at com.mysql.jdbc.Connection.createNewIO(Connection.java:2775)* at com.mysql.jdbc.Connection.<init>(Connection.java:1555)* at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:285)* at pub.ikkyu.utils.ConnectionUtil.create(ConnectionUtil.java:57)* at pub.ikkyu.pool.DatabaseConnectionPooledObjectFactory.create(DatabaseConnectionPooledObjectFactory.java:28)* at pub.ikkyu.pool.DatabaseConnectionPooledObjectFactory.create(DatabaseConnectionPooledObjectFactory.java:15)* at org.apache.commons.pool2.BaseKeyedPooledObjectFactory.makeObject(BaseKeyedPooledObjectFactory.java:62)* at org.apache.commons.pool2.impl.GenericKeyedObjectPool.create(GenericKeyedObjectPool.java:1041)* at org.apache.commons.pool2.impl.GenericKeyedObjectPool.borrowObject(GenericKeyedObjectPool.java:357)* at org.apache.commons.pool2.impl.GenericKeyedObjectPool.borrowObject(GenericKeyedObjectPool.java:279)* at pub.ikkyu.test.DatabaseConnectionPoolTest.main(DatabaseConnectionPoolTest.java:41)* Thu Sep 03 13:37:14 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.
com.mysql.jdbc.JDBC4Connection@7d9f158f* 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.cj.jdbc.ConnectionImpl@46268f08*/
}

注意点:

整个工具类代码最核心的部分如下:

// 保留原始ClassLoader
ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(loader);// 还原原始ClassLoader
Thread.currentThread().setContextClassLoader(oldClassLoader);

如果不设置当前线程的ClassLoader,则可能会出现ClassLoader默认为AppClassLoader,导致相同全限定名的类只会加载一次,从而达不到预期的目标的问题

单项目多JDBC驱动版本加载相关推荐

  1. Linux 系统支持blacklist的kernel版本加载上海域格 CLM920 NC2 mdg 模块的驱动示例

    CLM920_NC2 M.2 模块提供三路射频天线接口, 分别为主集天线, 分集天线, GPS 天线. CLM920_NC2 LTE M.2 模 块 是 一 款 PCI Express M.2 1.0 ...

  2. web项目的启动时文件加载顺序

    web项目的启动时文件加载顺序 转自:http://www.cnblogs.com/yw-ah/p/5751509.html 一. 1.启动一个WEB项目,WEB容器会先去读取它的配置文件web.xm ...

  3. java 对应sql驱动版本_有关sqlserver的 jdbc驱动版本整理

    原标题:有关sqlserver的 jdbc驱动版本整理 皕杰报表创建sqlserver数据库的时候,需要加载sqlserver jdbc驱动,有些时候你需要了解不同版本的sqlserver的 jdbc ...

  4. vue项目结构及启动文件加载过程分析

    vue项目结构及启动文件加载过程分析 一.vue项目结构 1.导入项目 准备好开发工具Visual Studio Code,导入生成的项目案例.我的Vue版本: 2.项目目录及文件说明 2.1.项目主 ...

  5. linux Pci字符驱动基本加载流程

    今天有朋友问我linux系统Pci字符驱动加载流程,简单整理了一下,顺便做个记录. 首先说下需要包含的头文件: 一个完整的字符驱动一般包含下面这些头文件: #include <linux/typ ...

  6. jdbc版本低MySQL版本高_Mysql JDBC驱动版本与Mysql版本的对应问题解决

    好长时间不用Mysql了, 昨天朋友有一个小项目在我的机器上跑的一点问题都没有, 到他的机器上却是报服务器内部错误(500), 用QQ上远程协助(太慢 好长时间不用Mysql了, 昨天朋友有一个小项目 ...

  7. 配置项目启动的时候就加载 servlet

    load-on-startup值为0 就是在项目启动的时候自动加载该servlet

  8. java web配置dll文件_JavaWeb项目中dll文件动态加载方法解析(详细步骤)

    相信很多做Java的朋友都有过用Java调用JNI实现调用C或C++方法的经历,那么Java Web中又如何实现DLL/SO文件的动态加载方法呢.今天就给大家带来一篇JAVA Web项目中DLL/SO ...

  9. “在解决方案中的一个或多个项目由于以下原因未能加载 项目文件或网站已移动或重新命名,或者不在您的计算机上” 的解决办法...

    用Visual studio.NET 2003/2005/2008在打开从网上下载的解决方案或从其他地方考来的解决方案时,往往会碰到一个这样的错误,提示说: "在解决方案中的一个或多个项目由 ...

最新文章

  1. java.lang.Thread 和 java.lang.Runnable的区别
  2. Debian 8.2 64位 安装MongoDB
  3. 计算机培训研修日志,2021年计算机培训研修日志
  4. Android之解决Android8.0手机(Notification)收不到自定义消息通知以及其它手机得到数据不同步
  5. [vue] 你知道v-model的原理吗?说说看
  6. 2天玩转单反相机引领时尚娱乐新生活
  7. 从零搭建 ES 搜索服务(六)相关性排序优化
  8. 设计模式必须遵守的六大原则
  9. 使用pdfviewer预览报错PDF.js v2.9.359 (build: e667c8cbc)信息:file origin does not match viewer‘s
  10. 开关稳压器设计的PCB布局布线
  11. J2SDK 安装配置指南
  12. Python中的Bunch模式
  13. 大一护理专业计算机挂科,大一就迎来这些难度大的学科,往年“挂科率”高,大一学生需重视...
  14. Windows平台下面MD5 SHA1 SHA256命令行工具
  15. firefly-rk3288开发板Linux驱动——LED驱动
  16. mbio期刊拒稿率_PLoS Pathogens
  17. 彻底征服 React.js + Flux + Redux【讲师辅导】-曾亮-专题视频课程
  18. AES.CBC 解密,python通用模板
  19. Web开发中的常见应用
  20. 排序算法九:基数排序

热门文章

  1. 爱奇艺迎史上最大裁员潮:总监说撸就撸,有的部门直接裁一半......
  2. node.js测试html tdd,nodejs的单元测试框架mocha
  3. CVPR 2022 | QueryDet:使用级联稀疏query加速高分辨率下的小目标检测
  4. JIL Mobile Widget 初步认识
  5. 移植caffe到hi3519
  6. 智慧数字经营小程序有什么好处
  7. JIT准时生产制造管理
  8. 2018这一年读过的那些书
  9. 1059:求平均年龄
  10. 屁股决定脑袋,思想决定高度