jdbc如何注册数据库驱动Driver的?
1. 先看看原生jdbc执行sql的步骤
// 在程序启动的时候需要注册一次mysql驱动,必须引入 mysql-connnector-java 的包
Class.forName("com.mysql.jdbc.Driver");
// 创建数据库连接
Connection connection = DriverManager.getConnection("jdbcUrl", "userName", "password");
// 创建执行语句
Statement statment = connection.createStatement();
// 执行sql,返回结果集
ResultSet resultSet = statement.executeQuery("select * from table;");
// 最后,关闭连接
connection.close();
2. 为啥 Class.forName("com.mysql.jdbc.Driver") 加载一下类就能注册驱动了?
看看 com.mysql.jdbc.Driver 类的代码 (版本5.1.47)
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!");
}
}
当类加载的时候会执行静态代码块,从上面可以看到,mysql的Driver静态代码块里边 将自己注册到 DriverManager 里了; 所以后续的 DriverManager#getConnection 的过程能使用到mysql的driver
3. DriverManager 在上述jdbc执行过程中的作用
3.1 DriverManager.registerDriver 显式/主动注册驱动
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>(); //驱动列表
public static synchronized void registerDriver(java.sql.Driver driver,
DriverAction da)
throws SQLException {
/* Register the driver if it has not already been added to our list */
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();
}
println("registerDriver: " + driver);
}
registeredDrivers.addIfAbsent(new DriverInfo(driver, da)); 驱动的注册过程就是将其加入到 一个 copyOnWrite 的列表里;
3.2 DriverManager#getConnection 连接创建过程
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
4. DriverManager 自动加载驱动
除了主动调用 DriverManager#registerDriver() 注册驱动外,DriverManager 其实还有两种自动加载驱动的机制:
- 系统变量 jdbc.drivers 定义的驱动, 然后内部也是通过 Class.forName 去注册这些驱动类
- 基于 ServiceLoader 的 service-provicer SPI 机制, 即数据库驱动提供方在其类路径下存在文件 /META-INF/services/java.sql.Driver 指定其驱动的实现类,就能被DriverManager自动加载到;
具体逻辑: 在 DriverManager 的静态代码块里,会执行 loadInitialDrivers() 一个自动驱动加载逻辑
private static void loadInitialDrivers() {
String drivers;
try {
//1. 加载系统变量 jdbc.drivers 设置的冒号隔开的驱动
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
// 2. service-provider 加载机制
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});
println("DriverManager.initialize: jdbc.drivers = " + drivers);
if (drivers == null || drivers.equals("")) {
return;
}
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
// 内部也是利用 Class.forName 去加载 jdbc.drivers 定义的Driver类名称
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}
总结
System.getProperty('jdbc.drivers") 这种方式个人觉得适合用于在应用初始化脚本中指定
基于 ServiceLoader 的SPI方式,只要符合 spi 标准的驱动包引入了就会自动加载
我们在基于jdbc实现或封装数据库连接池、中间件的时候,就不需要再 Class.forName 的方式去显示注册驱动了,特别是当需要支持多种数据库的时候,还得根据 jdbcUrl 去判断目标 driverClassName 是啥, 如果要再支持新的关系型数据库时候 还得再改代码
看看常用关系数据库厂商的 spi 支持情况
- mysql-connector-java 早在5.0.0(2005-12-22) 版本就添加了 META-INF/services/java.sql.Driver 文件支持 service-provicer SPI; 现在一般用8.x的版本
- pg connector 也在Version 42.2.13(2020-06-04) 之后支持
所以如果不用支持古董版本的驱动,基本可以放心直接 DriverManager#getConnection 去创建db连接了
jdbc如何注册数据库驱动Driver的?的更多相关文章
- jdbc 加载数据库驱动如何破坏双亲委托模式
导读 通过jdbc链接数据库,是每个学习Java web 方向的人必然一开始会写的代码,虽然现在各路框架都帮大家封装好了jdbc,但是研究一下jdbc链接的套路还是很意义 术语以及相 ...
- JDBC加载数据库驱动的方式
JDBC作为数据库访问的规范接口,其中只是定义一些接口.具体的实现是由各个数据库厂商来完成. 一.重要的接口: 1.public interface Driver 每个驱动程序类必须实现的接口.Jav ...
- JDBC 学习笔记(四)—— JDBC 加载数据库驱动,获取数据库连接
1. 加载数据库驱动 通常来说,JDBC 使用 Class 类的 forName() 静态方法来加载驱动,需要输入数据库驱动代表的字符串. 例如: 加载 MySQL 驱动: Class.forName ...
- jdbc 03:注册驱动的方式
jdbc连接mysql时,注册驱动的方式 package com.examples.jdbc.o3_注册驱动方式; //mysql驱动所在的包 import com.mysql.jdbc.Driver ...
- JDBC链接mysql数据库
Unit_1 首先:JDBC:java database connectivity SUN公司提供的一套操作数据库的标准规范. JDBC与数据库驱动的关系是接口与实现的关系. JDBC涉及到四个核心的 ...
- Java -- JDBC 学习--获取数据库链接
数据持久化 持久化(persistence): 把数据保存到可掉电式存储设备中以供之后使用.大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固化”,而持久化的实现过程大 ...
- 在java程序中使用JDBC连接mysql数据库
在java程序中我们时常会用到数据库中的数据或操作数据库中的数据,如果java程序没有和我们得数据库连接,就不能实现在java程序中直接操作数据库.使用jdbc就能将java程序和数据库连起来,此时我 ...
- Java第三十五天,用JDBC操作MySQL数据库(一),基础入门
一.JDBC的概念 Java DataBase Connectivity 从字面意思我们也不难理解,就是用Java语言连接数据库的意思 JDBC定义了Java语言操作所有关系型数据库的规则(接口).即 ...
- JDBC | 第一章: 快速开始使用JDBC连接Mysql数据库?
开始使用基于java的JDBC技术来连接mysql进行msyql数据库简单的CRUD操作 下载对应mysql驱动包 这里我创建maven项目基于maven下载 <!--mysql 驱动--> ...
随机推荐
- 收集雪花(map)
题目描述 不同的雪花往往有不同的形状.在北方的同学想将雪花收集起来,作为礼物送给在南方的同学们.一共有n个时刻,给出每个时刻下落雪花的形状,用不同的整数表示不同的形状.在收集的过程中,同学们不希望有重 ...
- Java核心基础第2篇-Java基本语法
Java基本语法 本章一起来探讨下Java的基本语法.主要从以下几个方面展开: Java关键字 Java标识符 Java变量 Java数据类型 Java运算符 学完本章内容之后,我们对Java会有更深 ...
- webrtc之TURE、STUN、摄像头打开实战
前言: 大家周末好,今天给 webrtc之TURE.STUN.摄像头打开实战 大家分享的是webrtc第一篇文章,在之前的音视频文章里面没有分享过关于webrtc的内容:在上个周末分享了一篇关于播放器 ...
- XCTF csaw2013reversing2
题目描述:听说运行就能拿到Flag,不过菜鸡运行的结果不知道为什么是乱码 一.先运行看看. 果然乱码. 二.查壳 三.是pe文件,可以拖入od和ida进行动态和静态分析. 1.对主函数进行反编译一下. ...
- ADO.NET整理 [转]
虽然我们都知道ADO.NET是对数据库的操作,但是要真的说出ADO.NET的具体含义还不是很容易. ADO.NET是ActiveX Data Objects的缩写,它是一个COM组件库,用于在micr ...
- 深度解析HashMap底层实现架构
摘要:分析Map接口的详细使用以及HashMap的底层是如何实现的? 本文分享自华为云社区<[图文并茂]深度解析HashMap高频面试及底层实现结构![奔跑吧!JAVA]>,原文作者:灰小 ...
- python 14篇 写日志
import sys from loguru import logger # 日志级别: debug 调试信息打印日志比较详细,级别最低 # info 正常的提示信息,级别较低 # waring 警告 ...
- c语言:sprintf() 数字转字符赋值给数组
//sprintf() //sprintf 最常见的应用之一是把整数打印到字符串中,所以,spritnf 在大多数场合可以替代itoa /* 缓冲区溢出 第一个参数的长度太短了,没的说,给个大点的地方 ...
- 实验4 RDD编程初级实践
1.spark-shell交互式编程 (1) 该系总共有多少学生 scala> val lines = sc.textFile("file:///usr/local/spark/spa ...
- 【深度学习】在linux和windows下anaconda+pycharm+tensorflow+cuda的配置
在linux和windows下anaconda+pycharm+tensorflow+cuda的配置 在linux和windows下anaconda+pycharm+tensorflow+cuda的配 ...