当前位置: 首页 > news >正文

JDBC中驱动加载的过程分析(下)

转自:http://miaoxiaodong78.blog.163.com/blog/static/1876513620061165043300/

本篇主要用几个开源数据库的驱动讲述驱动是如何加载的,以及“可插拔”机制等。
由于文章篇幅有限,本文章并不是专门研究开源源代码!因此我们仅研究那些与数据库驱动加载直接相关的方法。在下面的几个开源软件的驱动中,我们主要关注驱动类的getConnection()方法。
一、几个开源数据库的驱动类
以下第一个是smallsql中驱动类SSDriver的源代码:

package smallsql.database;
import java.sql.*;
import java.util.Properties;
public class SSDriver implements Driver {
static SSDriver drv;
static {
try{
drv = new SSDriver();
java.sql.DriverManager.registerDriver(drv);
}catch(Throwable e){}
}
public Connection connect(String url, Properties info) 
                          throws SQLException {
if(!acceptsURL(url)) return null;
……
return new SSConnection( (idx > 0) ? url.substring(idx+1) : null);
}
……
}
从上面红色的部分可以看到:这是一个静态语句块(static block),这意味着该语句是在类构造完成前完成的(关于语句块的加载请阅读《Think in java》)。即调用class.forName(“smallsql.database.SSDriver”)语句时,会首先创建一个SSDriver的实例,并且将其向驱动管理器(DriverManager)注册。这样就完成驱动的注册了。
从上面的蓝色的代码可以看出:驱动的连接方法返回的是一个具体的SSConnection对象。而在前面研究的Driver接口中返回的是Connection接口,这是不茅盾的,SSConnection对象实现了Connection接口。
再下面一个是HSqlD中的驱动类的源代码:
package org.hsqldb;
import java.sql.*;
import java.util.Properties;
import org.hsqldb.jdbc.jdbcConnection;
import org.hsqldb.persist.HsqlDatabaseProperties;
import org.hsqldb.persist.HsqlProperties;
public class jdbcDriver implements Driver {

public Connection connect(String url, Properties info) 
                          throws SQLException {
return getConnection(url, info);
}
public static Connection getConnection(String url, Properties info) 
                                       throws SQLException {
HsqlProperties props = DatabaseURL.parseURL(url, true);
if (props == null) {
throw new SQLException(Trace.getMessage(Trace.INVALID_JDBC_ARGUMENT));
} else if (props.isEmpty()) {
return null;
}
props.addProperties(info);
return new jdbcConnection(props);
}
static {
try {
DriverManager.registerDriver(new jdbcDriver());
} catch (Exception e) {}
}
}
蓝色的依然是建立连接的部分,依然返回某个具体的实现java.sql.Connection接口的对象。是设计模式中的哪个工厂啊(一般工厂、抽象工厂、工厂方法还是什么都不是啊)!
红色的同样是一个静态语句块,同样完成该驱动的注册。
两个开源数据库的驱动竟然如此相似,是不是其它的就不一样呢!看下面是mckoi中的驱动类:
package com.mckoi;
public class JDBCDriver extends com.mckoi.database.jdbc.MDriver {
static {
com.mckoi.database.jdbc.MDriver.register();
}
public JDBCDriver() {
super();
// Or we could move driver registering here...
}
}
红色的部分又是完成相同的工作――在类装载完成前向驱动管理器注册驱动,只不过这次是调用MDriver的register方法罢了。那么该驱动建立连接是否也一样呢,当然一样啦!不信你看下面的代码:
package com.mckoi.database.jdbc;
……
public class MDriver implements Driver {
……
private static boolean registered = false;
public synchronized static void register() {
if (registered == false) {
try {
java.sql.DriverManager.registerDriver(new MDriver());
registered = true;
}catch (SQLException e) {
e.printStackTrace();
}
}
}
public Connection connect(String url, Properties info) 
                         throws SQLException {
if (!acceptsURL(url)) { return null; }
DatabaseInterface db_interface;
int row_cache_size;
int max_row_cache_size;
……
MConnection connection = new MConnection(url, db_interface, row_cache_size, max_row_cache_size);
……
return connection;
}
}


从以上三个开源数据库驱动的源代码可以看出:在调用Class.forName(“XXXDriver”)时,完成了将具体的驱动程序向JDBC API中驱动管理器的注册,该注册方法在类构造完成前完成,一般使用静态语句块。在调用DriverManager的getConnection方法时,一般先在已注册的驱动中查找可以了解此URL的驱动,然后调用该驱动的connect方法,从而建立连接,返回的连接都是一个实现java.sql.Connection接口的具体类。下面是该过程的时序图。
二、JDBC中驱动加载的时序图


以上是JDBC中驱动加载的时序图。时序图主要有以下7个动作:
1. 客户调用Class.forName(“XXXDriver”)加载驱动。
2. 此时此驱动类首先在其静态语句块中初始化此驱动的实例,
3. 再向驱动管理器注册此驱动。
4. 客户向驱动管理器DriverManager调用getConnection方法,
5. DriverManager调用注册到它上面的能够理解此URL的驱动建立一个连接,
6. 在该驱动中建立一个连接,一般会创建一个对应于数据库提供商的XXXConnection连接对象,
7. 驱动向客户返回此连接对象,不过在客户调用的getConnection方法中返回的为一个java.sql.Connection接口,而具体的驱动返回一个实现java.sql.Connection接口的具体类。
以上就是驱动加载的全过程。由此过程我们可以看出JDBC的其它一些特点。
三、JDBC的架构
在《教你建立简单JDBC程序》一篇中,讲述了一般JDBC的几个步骤。通过本篇的介绍,我将此程序分为以下几部分:


上图中,蓝色的即为本章前面介绍的JDBC驱动加载的细节部分。看看下面的部分:左面的很明显吧!是java.sql包中的接口吧!它是抽象的!右边呢?通过驱动管理器DriverManager得到的是一个实现java.sql.Connection接口的具体类吧!(不知道啊!前面不是讲过了吗!)因此我们可以可以注意到左右分别是抽象的和具体的。(这种抽象和具体的连接是由java的RTTI支持的,不懂可以阅读《Think in java》)。在接下来的结果集的处理rs也是抽象的吧!
因此,在写JDBC程序时,即使我们使用不同数据库提供商的数据库我们只要改变驱动类的地址,和具体连接的URL及其用户名和密码,其它几乎不用任何修改,就可以完成同样的工作!方便吧!
这意味着什么呢?我们其实是在针对抽象接口编程,只要知道接口的调用顺序,以及其中的主要方法,我们就可以迅速学会JDBC编程了!
同时,我们只要对不同数据库提供商的驱动类使用Class.forName(“XXXDriver”)就可以加载驱动,其细节你根本不用关注――JDBC Framework已经全为你搞定了!应用程序对于不同提供商的数据库是无需太多改动的,因而其是“可插拔”的!整个J2EE就是一个高层的API――抽象接口,用户可以使
用不同的产品提供商提供的产品,使用统一的API,从而便于程序员学习!
谢谢大家!到此结束!

相关文章:

  • 条件控制语句
  • hibernate重要接口说明
  • 微信开发个人总结
  • Hibernate中对象的三种状态及相互转化
  • hibernate脏数据检查
  • 版本控制-Git服务器搭建和常用命令使用
  • hibernate中get与load的区别
  • 双11_2016
  • hibernate中的Session.flush()
  • hibernate的lazy机制
  • hibernate的lazy策略使用总结
  • springmvc-spring-mybatis实现最简单的登录验证
  • hibernate 的lazy策略存在的问题
  • hibernate中的悲观锁和乐观锁
  • hibernate的查询缓存机制
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • canvas 高仿 Apple Watch 表盘
  • export和import的用法总结
  • Joomla 2.x, 3.x useful code cheatsheet
  • jquery cookie
  • jQuery(一)
  • puppeteer stop redirect 的正确姿势及 net::ERR_FAILED 的解决
  • ViewService——一种保证客户端与服务端同步的方法
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 从伪并行的 Python 多线程说起
  • 构造函数(constructor)与原型链(prototype)关系
  • 基于OpenResty的Lua Web框架lor0.0.2预览版发布
  • 老板让我十分钟上手nx-admin
  • 前端技术周刊 2019-02-11 Serverless
  • 如何将自己的网站分享到QQ空间,微信,微博等等
  • 如何借助 NoSQL 提高 JPA 应用性能
  • 什么软件可以提取视频中的音频制作成手机铃声
  • 实现简单的正则表达式引擎
  • 微信端页面使用-webkit-box和绝对定位时,元素上移的问题
  • 在weex里面使用chart图表
  • ionic异常记录
  • Java数据解析之JSON
  • Java性能优化之JVM GC(垃圾回收机制)
  • ​TypeScript都不会用,也敢说会前端?
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • #!/usr/bin/python与#!/usr/bin/env python的区别
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • (10)STL算法之搜索(二) 二分查找
  • (14)目标检测_SSD训练代码基于pytorch搭建代码
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (附源码)spring boot基于小程序酒店疫情系统 毕业设计 091931
  • (三)docker:Dockerfile构建容器运行jar包
  • (原創) 如何刪除Windows Live Writer留在本機的文章? (Web) (Windows Live Writer)
  • (转)http协议
  • .mysql secret在哪_MYSQL基本操作(上)
  • .NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布
  • .net core 6 使用注解自动注入实例,无需构造注入 autowrite4net
  • .Net Core 中间件验签
  • .NET Entity FrameWork 总结 ,在项目中用处个人感觉不大。适合初级用用,不涉及到与数据库通信。
  • .NET 使用配置文件