前言

最近在研究Mybatis框架,由于该框架基于JDBC,想要很好地理解和学习Mybatis,必须要对JDBC有较深入的了解。所以便把JDBC 这个东东翻出来,好好总结一番,作为自己的笔记,也是给读者一个参考~~~以下是本文的组织结构,读者可以点击上面的目录查看:

概述

一般情况下,在应用程序中进行数据库连接,调用JDBC接口,首先要将特定厂商的JDBC驱动实现加载到系统内存中,然后供系统使用。基本结构图如下:

驱动加载入内存的过程

这里所谓的驱动,其实就是实现了Java.sql.Driver接口的类。如Oracle的驱动类是 oracle.jdbc.driver.OracleDriver.class(此类可以在oracle提供的JDBC jar包中找到),此类实现了java.sql.Driver接口。

由于驱动本质上还是一个class,将驱动加载到内存和加载普通的class原理是一样的:使用Class.forName("driverName")。以下是将常用的数据库驱动加载到内存中的代码:

[java] view plain copy  print?
  1. //加载Oracle数据库驱动
  2. Class.forName("oracle.jdbc.driver.OracleDriver");
  3. //加载SQL Server数据库驱动
  4. Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
  5. //加载MySQL 数据库驱动
  6. Class.forName("com.mysql.jdbc.Driver");

         注意:Class.forName()将对应的驱动类加载到内存中,然后执行内存中的static静态代码段,代码段中,会创建一个驱动Driver的实例,放入DriverManager中,供DriverManager使用。

      例如,在使用Class.forName() 加载oracle的驱动oracle.jdbc.driver.OracleDriver时,会执行OracleDriver中的静态代码段,创建一个OracleDriver实例,然后调用DriverManager.registerDriver()注册:

[java] view plain copy  print?
  1. static {
  2. Timestamp localTimestamp = Timestamp.valueOf("2000-01-01 00:00:00.0");
  3. try {
  4. if (defaultDriver == null) {
  5. //创建一个OracleDriver实例,然后注册到DriverManager中
  6. defaultDriver = new OracleDriver();
  7. DriverManager.registerDriver(defaultDriver);
  8. }
  9. } catch (RuntimeException localRuntimeException) {
  10. } catch (SQLException localSQLException) {
  11. }


Driver的功能

java.sql.Driver接口规定了Driver应该具有以下功能:

其中:

acceptsURL(String url) 方法用来测试对指定的url,该驱动能否打开这个url连接。driver对自己能够连接的url会制定自己的协议,只有符合自己的协议形式的url才认为自己能够打开这个url,如果能够打开,返回true,反之,返回false;

例如:oracle定义的自己的url协议如下:

jdbc:oracle:thin:@//<host>:<port>/ServiceName

jdbc:oracle:thin:@<host>:<port>:<SID>

oracle自己的acceptsURL(String url)方法如下:

[java] view plain copy  print?
  1. public boolean acceptsURL(String paramString) {
  2. if (paramString.startsWith("jdbc:oracle:")) {
  3. return (oracleDriverExtensionTypeFromURL(paramString) > -2);
  4. }
  5. return false;
  6. }
  7. private int oracleDriverExtensionTypeFromURL(String paramString) {
  8. int i = paramString.indexOf(58) + 1;
  9. if (i == 0) {
  10. return -2;
  11. }
  12. int j = paramString.indexOf(58, i);
  13. if (j == -1) {
  14. return -2;
  15. }
  16. if (!(paramString.regionMatches(true, i, "oracle", 0, j - i))) {
  17. return -2;
  18. }
  19. ++j;
  20. int k = paramString.indexOf(58, j);
  21. if (k == -1) {
  22. return -3;
  23. }
  24. String str = paramString.substring(j, k);
  25. if (str.equals("thin")) {
  26. return 0;
  27. }
  28. if ((str.equals("oci8")) || (str.equals("oci"))) {
  29. return 2;
  30. }
  31. return -3;
  32. }

由上可知oracle定义了自己应该接收什么类型的URL,自己能打开什么类型的URL连接(注意:这里acceptsURL(url)只会校验url是否符合协议,不会尝试连接判断url是否有效) 。拓展阅读:常用数据库 JDBC URL格式

connect(String url,Properties info)方法,创建Connection对象,用来和数据库的数据操作和交互,而Connection则是真正数据库操作的开始(在此方法中,没有规定是否要进行acceptsURL()进行校验)。

手动加载驱动 Driver 并实例化进行数据库操作的例子

[java] view plain copy  print?
  1. public static void driverTest(){
  2. try {
  3. //1.加载oracle驱动类,并实例化
  4. Driver driver = (Driver) Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
  5. //2.判定指定的URL oracle驱动能否接受(符合oracle协议规则)
  6. boolean flag = driver.acceptsURL("jdbc:oracle:thin:@127.0.0.1:1521:xe");
  7. //标准协议测试
  8. boolean standardFlag1 = driver.acceptsURL("jdbc:oracle:thin:@//<host>:<port>/ServiceName");
  9. boolean standardFlag2 = driver.acceptsURL("jdbc:oracle:thin:@<host>:<port>:<SID>");
  10. System.out.println("协议测试:"+flag+"\t"+standardFlag1+"\t"+standardFlag2);
  11. //3.创建真实的数据库连接:
  12. String  url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";
  13. Properties props = new Properties();
  14. props.put("user", "louluan");
  15. props.put("password", "123456");
  16. Connection connection = driver.connect(url, props);
  17. //connection 对象用于数据库交互,代码省略。。。。。
  18. } catch (Exception e) {
  19. System.out.println("加载Oracle类失败!");
  20. e.printStackTrace();
  21. } finally{
  22. }
  23. }

上述的手动加载Driver并且获取连接的过程稍显笨拙:如果现在我们加载进来了多个驱动Driver,那么手动创建Driver实例,并根据URL进行创建连接就会显得代码杂乱无章,并且还容易出错,并且不方便管理。JDBC中提供了一个DriverManager角色,用来管理这些驱动Driver。

DriverManager角色

事实上,一般我们操作Driver,获取Connection对象都是交给DriverManager统一管理的。DriverManger可以注册和删除加载的驱动程序,可以根据给定的url获取符合url协议的驱动Driver或者是建立Conenction连接,进行数据库交互。

以下是DriverManager的关键方法摘要:

DriverManager 内部持有这些注册进来的驱动 Driver,由于这些驱动都是 java.sql.Driver 类型,那么怎样才能获得指定厂商的驱动Driver呢?答案就在于:

java.sql.Driver接口规定了厂商实现该接口,并且定义自己的URL协议。厂商们实现的Driver接口通过acceptsURL(String url)来判断此url是否符合自己的协议,如果符合自己的协议,则可以使用本驱动进行数据库连接操作,查询驱动程序是否认为它可以打开到给定 URL 的连接。

使用DriverManager获取指定Driver

对于驱动加载后,如何获取指定的驱动程序呢?这里,DriverManager的静态方法getDriver(String url)可以通过传递给的URL,返回可以打开此URL连接的Driver。
     比如,我想获取oracle的数据库驱动,只需要传递形如jdbc:oracle:thin:@<host>:<port>:<SID>或者jdbc:oracle:thin:@//<host>:<port>/ServiceName的参数给DriverManager.getDriver(String url)即可:

[java] view plain copy  print?
  1. Driver oracleDriver =DriverManager.getDriver("jdbc:oracle:thin:@<host>:<port>:<SID>");

实际上,DriverManager.getDriver(String url)方法是根据传递过来的URL,遍历它维护的驱动Driver,依次调用驱动的Driver的acceptsURL(url),如果返回acceptsURL(url)返回true,则返回对应的Driver:

[java] view plain copy  print?
  1. public static Driver getDriver(String paramString) throws SQLException {
  2. //省略部分代码。。。。
  3. Iterator localIterator = registeredDrivers.iterator();
  4. //遍历注册的驱动
  5. while (localIterator.hasNext()) {
  6. DriverInfo localDriverInfo = (DriverInfo) localIterator.next();
  7. if (isDriverAllowed(localDriverInfo.driver, localClass))
  8. try {
  9. //如果accepsURL() 为true,返回对应的driver
  10. if (localDriverInfo.driver.acceptsURL(paramString)) {
  11. //返回对应的driver
  12. return localDriverInfo.driver;
  13. }
  14. } catch (SQLException localSQLException) {
  15. }
  16. else
  17. println("    skipping: "+ localDriverInfo.driver.getClass().getName());
  18. }
  19. throw new SQLException("No suitable driver", "08001");
  20. //-----省略部分代码
  21. }

使用DriverManager注册和取消注册驱动Driver

在本博文开始的 驱动加载的过程一节中,讨论了当使用Class.forName("driverName")加载驱动的时候,会向DriverManager中注册一个Driver实例。以下代码将验证此说法:

[java] view plain copy  print?
  1. public static void defaultDriver(){
  2. try {
  3. String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";
  4. //1.将Driver加载到内存中,然后执行其static静态代码,创建一个OracleDriver实例注册到DriverManager中
  5. Class.forName("oracle.jdbc.driver.OracleDriver");
  6. //取出对应的oracle 驱动Driver
  7. Driver driver  = DriverManager.getDriver(url);
  8. System.out.println("加载类后,获取Driver对象:"+driver);
  9. //将driver从DriverManager中注销掉
  10. DriverManager.deregisterDriver(driver);
  11. //重新通过url从DriverManager中取Driver
  12. driver  = DriverManager.getDriver(url);
  13. System.out.println(driver);
  14. } catch (Exception e) {
  15. System.out.println("加载Oracle类失败!");
  16. e.printStackTrace();
  17. } finally{
  18. }
  19. }

以上代码主要分以下几步:

1. 首先是将  oracle.jdbc.driver.OracleDriver加载到内存中;

2.  然后便调用DriverManager.getDriver()去取Driver实例;

3.  将driver实例从DriverManager中注销掉;

4.尝试再取 对应url的Driver实例;

上述代码执行的结果如下:

从执行结果看,正好能够验证以上论述:当第四步再次获取对应url的 Driver 实例时,由于已经被注销掉了,找不到适当的驱动Driver,抛出了 "Not suitable driver" 的异常。

将上述的例子稍作变化,在注销掉了静态块创建的driver后,往DriverManager注册一个自己创建的Driver对象实例(具体步骤请看注释):

[java] view plain copy  print?
  1. public static void defaultDriver(){
  2. try {
  3. String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";
  4. //1.将Driver加载到内存中,然后执行其static静态代码,创建一个OracleDriver实例注册到DriverManager中
  5. Driver dd = (Driver)Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
  6. //2.取出对应的oracle 驱动Driver
  7. Driver driver  = DriverManager.getDriver(url);
  8. System.out.println("加载类后,获取Driver对象:"+driver);
  9. //3. 将driver从DriverManager中注销掉
  10. DriverManager.deregisterDriver(driver);
  11. //4.此时DriverManager中已经没有了驱动Driver实例,将创建的dd注册到DriverManager中
  12. DriverManager.registerDriver(dd);
  13. //5.重新通过url从DriverManager中取Driver
  14. driver  = DriverManager.getDriver(url);
  15. System.out.println("注销掉静态创建的Driver后,重新注册的Driver:    "+driver);
  16. System.out.println("driver和dd是否是同一对象:" +(driver==dd));
  17. } catch (Exception e) {
  18. System.out.println("加载Oracle类失败!");
  19. e.printStackTrace();
  20. } finally{
  21. }
  22. }

以下代码运行的结果:

以上代码先创建了一个Driver对象,在注销了DriverManager中由加载驱动过程中静态创建驱动之后,注册到系统中,现在DriverManager中对应url返回的Driver 即是在代码中创建的Driver对象。

使用DriverManager创建 Connection 连接对象

创建 Connection 连接对象,可以使用驱动Driver的 connect(url,props),也可以使用 DriverManager 提供的getConnection()方法,此方法通过url自动匹配对应的驱动Driver实例,然后调用对应的connect方法返回Connection对象实例。

[java] view plain copy  print?
  1. Driver driver  = DriverManager.getDriver(url);
  2. Connection connection = driver.connect(url, props);

上述代码等价于:

[java] view plain copy  print?
  1. Class.forName("oracle.jdbc.driver.OracleDriver");
  2. Connection connection = DriverManager.getConnection(url, props);

jdbc.drivers

DriverManager 作为 Driver 的管理器,它在第一次被使用的过程中(即在代码中第一次用到的时候),它会被加载到内存中,然后执行其定义的static静态代码段,在静态代码段中,有一个  loadInitialDrivers()  静态方法,用于加载配置在jdbc.drivers 系统属性内的驱动Driver,配置在jdbc.drivers 中的驱动driver将会首先被加载:

[java] view plain copy  print?
  1. static {
  2. loadInitialDrivers();//加载配置在jdbc.drivers系统变量中的驱动driver
  3. println("JDBC DriverManager initialized");
  4. SET_LOG_PERMISSION = new SQLPermission("setLog");
  5. }
[java] view plain copy  print?
  1. private static void loadInitialDrivers() {
  2. String str1;
  3. try {
  4. str1 = (String) AccessController
  5. .doPrivileged(new PrivilegedAction() {
  6. public String run() {
  7. return System.getProperty("jdbc.drivers");//返回jdbc.drivers值
  8. }
  9. });
  10. } catch (Exception localException1) {
  11. str1 = null;
  12. }
  13. //省略部分代码......
  14. if ((str1 == null) || (str1.equals("")))
  15. return;
  16. String[] arrayOfString1 = str1.split(":");
  17. println("number of Drivers:" + arrayOfString1.length);
  18. for (String str2 : arrayOfString1)
  19. try {
  20. //Class.forName加载对应的driver
  21. Class.forName(str2, true, ClassLoader.getSystemClassLoader());
  22. } catch (Exception localException2) {
  23. println("DriverManager.Initialize: load failed: "
  24. + localException2);
  25. }
  26. }

以下是通过jdbc.drivers加载驱动driver的方式:

[java] view plain copy  print?
  1. String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";
  2. //设置值系统变量jdbc.drivers
  3. System.setProperty("jdbc.drivers", "oracle.jdbc.driver.OracleDriver");
  4. //2.通过特定的url获取driver
  5. Driver driver = DriverManager.getDriver(url);
  6. //打印是否存在
  7. System.out.println(driver);

请记住:一定要在第一次使用DriverManager之前设置jdbc.drivers,因为DriverManager中的static静态代码段只会被执行一次!

-------------------------------------------------------------------------------------------------------------------------------------------------------------

以上是本文 老调重弹:JDBC系列 之 <驱动加载原理全面解析>的全部内容,以上是自己心得,并非权威,如有不妥或者此错误之处,欢迎读者批评和斧正!  欢迎关注我的下一篇博文: 老调重弹:JDBC系列 之 <JDBC层次结构和基本构成>


原文地址:http://blog.csdn.net/luanlouis/article/details/29850811


老调重弹:JDBC系列 之 驱动加载原理全面解析相关推荐

  1. 老调重弹:JDBC系列之驱动加载原理全面解析)

    前言 最近在研究Mybatis框架,由于该框架基于JDBC,想要很好地理解和学习Mybatis,必须要对JDBC有较深入的了解.所以便把JDBC 这个东东翻出来,好好总结一番,作为自己的笔记,也是给读 ...

  2. mysql驱动加载原理_老调重弹:JDBC系列 之 lt;驱动载入原理全面解析gt;

    前言 近期在研究Mybatis框架,因为该框架基于JDBC.想要非常好地理解和学习Mybatis,必需要对JDBC有较深入的了解.所以便把JDBC 这个东东翻出来.好好总结一番,作为自己的笔记,也是给 ...

  3. 驱动加载INF文件解析(一)

    INF是Device INFormation File的英文缩写,是Microsoft公司为硬件设备制造商发布其驱动程序推出的一种文件格式,INF文件中包含硬件设备的信息或脚本以控制硬件操作.在INF ...

  4. JDBC中驱动加载的过程分析

    JDBC中驱动加载的过程分析 作者:kenty  来源:博客园  发布时间:2007-08-20 15:01  阅读:1100 次  原文链接   [收藏]    本篇从java.sql.Driver ...

  5. (DT系列四)驱动加载中, 如何取得device tree中的属性

    转载于: http://blog.csdn.net/lichengtongxiazai/article/details/38941933 本文以At91rm9200平台为例,从源码实现的角度来分析驱动 ...

  6. JDBC驱动加载机制详解以及spi机制

    首先有两个问题: 1.java连接数据库时是否真的需要加载驱动? 2.JDBC如何区分多个驱动? 以下摘自:https://blog.csdn.net/buqutianya/article/detai ...

  7. HarmonyOS镜像,HarmonyOS驱动加载过程分析

    HDF(硬件驱动程序基础)驱动程序框架为驱动器开发人员提供驱动程序框架功能,包括驱动器加载,驱动服务管理和驱动程序消息传递机制.它旨在构建一个统一的驱动器架构平台,为驾驶开发人员提供更准确,更高效的开 ...

  8. mybatis 的懒加载原理

    断断续续的阅读 mybatis 的源码有好几个月了,想把自己了解到的一些东西与大家分享.今天给大家分享一下 mybatis 的懒加载原理. mybatis 的懒加载过程挺复杂的,涉及到的东西有很多,包 ...

  9. mysql驱动如何编写_解读MySQL驱动加载逻辑

    解读MySQL驱动加载逻辑 我们很早之前就知道最基础的JDBC编写,先执行Class.forName方法,加载MySQL驱动.但是为什么加载过驱动后,后续的接口层的调用就会自动切换到MySQL的相关代 ...

最新文章

  1. 美国国家政策基金会:美国科技秘密武器来自中国和印度
  2. 独家 | 使用机器学习对非结构化数据加速查询-第2部分(具有统计保证的近似选择查询)...
  3. 什么是schema?
  4. 省带宽、耗电小,腾讯游戏学院专家解析手游渲染架构
  5. Imation亏损额急剧增长 CEO仍表示“成功”
  6. 表面配准论文1--基于高阶图匹配方法的稠密表面配准
  7. 前端开发注意事项(HTML与CSS进阶)
  8. esxi挂载Linux的nfs盘,ESXi安装centos7挂载群晖NFS
  9. django 1.8 官方文档翻译: 1-2-5 编写你的第一个Django应用,第5部分
  10. IOS工作笔记001---windows下安装通过VmWare来安装IOS系统_并连接上网_来吧超级详细
  11. 使用POI导出Excel公用类方法 v1.1
  12. MATLAB中FFT的使用说明(含MATLAB代码)
  13. C/C++:构建你自己的插件框架(1)
  14. 卸载ncurses_linux 安装,卸载 监控服务
  15. 0.1+0.2为什么不等于0.3
  16. 上网被阻断未经pppoe认证_锐捷上网认证常见问题及解决办法
  17. php 模态框效果,评论:超酷的模态框效果 - Nifty
  18. html评价标准展示,网页设计评分标准.doc
  19. 开发android怎么开发ios app,怎么开发ios android 都能使用的app
  20. Web APIs /APIs --DOM简述/DOM中获取元素方法/事件(含鼠标事件)/操作(含案例)

热门文章

  1. 晋级 7 问 - 技术晋级答辩中常见问题解析
  2. 《Java语言程序设计与数据结构(基础篇)》第11版第四章复习题答案
  3. java创建画板_Java版画板的实现方法
  4. 华为微认证——鲲鹏处理器
  5. 视频转码之后md5会被修改吗
  6. bzoj3083 遥远的国度 bzoj3626 LCA (树链剖分)
  7. ublox8 M8协议介绍
  8. Connection reset原因分析及解决思路
  9. SimpleITK笔记本中ITK细分(Segmentation)介绍
  10. iOS上二维码和一维码识别系列一