JOBC

姓名:陈柳静  班级:软件151

上篇文章《再说Java EE》说明了一下什么是规范,有什么作用,这篇文章来细说一下JDBC。
JDBC
JDBC(Java Database Connection)也是Java EE中的一个规范,所谓规范是一组接口,如JDBC接口包含在java.sql及javax.sql包中,其中java.sql属于JavaSE,javax.sql属于JavaEE。

源码分析
    java.sql下有48个类,javax.sql下有45个类,展开分析不太现实,本文仅分析两个类,DriverManager和Driver。不知大家注意过这个问题没有,JDBC是接口,数据库驱动是实现,那么你编写的项目是如何找到实现的呢?
    控制台输出
    为了可以看到驱动加载过程中输出的日志,在加载驱动Class.forName("com.mysql.jdbc.Driver")之前,加上一句:
    DriverManager.setLogWriter(new java.io.PrintWriter(System.out));
    即可在控制台中看到输出。
    加载驱动
    驱动使用很简单,将数据库驱动放到项目的lib中,在代码中写入:
    Class.forName("com.mysql.jdbc.Driver");
    如果使用框架,如Hebernate配置文件中写入:
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

很明显,这两种方式都是使用反射加载驱动程序,我们来看一下驱动程序Driver的源代码,以mysql-connector-java-3.1.13为例:
package com.mysql.jdbc;

import java.sql.SQLException;

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!");
}
}

/**
 * Construct a new driver and register it with DriverManager
 * @throws SQLException
 *             if a database error occurs.
 */
public Driver() throws SQLException {
// Required for Class.forName().newInstance()
}
}
    核心代码就是那段静态代码块(static{}的意思是在类加载时执行一次,并且仅此一次),可以看到静态代码断的意思是将此Driver类实例化后注册到JDBC的java.sql.DriverManager类中,所以再来看一下JDBC的DriverManager.registerDriver

* Registers the given driver with the <code>DriverManager</code>.
* A newly-loaded driver class should call
* the method <code>registerDriver</code> to make itself
* known to the <code>DriverManager</code>.
*
* @param driver the new JDBC Driver that is to be registered with the
* <code>DriverManager</code>
* @exception SQLException if a database access error occurs
*/
public static synchronized void registerDriver(java.sql.Driver driver)
throws SQLException {
if (!initialized) {
initialize();
}

DriverInfo di = new DriverInfo();

di.driver = driver;
di.driverClass = driver.getClass();
di.driverClassName = di.driverClass.getName();

// Not Required -- drivers.addElement(di);

writeDrivers.addElement(di);
println("registerDriver: " + di);

/* update the read copy of drivers vector */
readDrivers = (java.util.Vector) writeDrivers.clone();

}
即可将com.mysql.jdbc.Driver添加到DriverManager的成员变量readDrivers中,以后获取数据库连接需要这个变量的帮助。

看上面的代码发现,还调用了initialize(),查看initialize()的源码看到它调用loadInitialDrivers(),这个函数的主要作用是加载JDBC默认驱动,registerDriver执行完,控制台的输出语句为:
JdbcOdbcDriver class loaded
registerDriver: driver[className=sun.jdbc.odbc.JdbcOdbcDriver,[email protected]]
DriverManager.initialize: jdbc.drivers = null
JDBC DriverManager initialized
registerDriver: driver[className=com.mysql.jdbc.Driver,[email protected]]
    可以看到先加载JdbcOdbcDriver,再加载我们加入的MySQL的driver。
    获取链接
    加载驱动完毕后,如何获取连接,继续看DriverManager的getConnection():
    //  Worker method called by the public getConnection() methods.
    private static Connection getConnection(
String url, java.util.Properties info, ClassLoader callerCL) throws SQLException {
java.util.Vector drivers = null;
        /*
 * When callerCl is null, we should check the application‘s
 * (which is invoking this class indirectly)
 * classloader, so that the JDBC driver class outside rt.jar
 * can be loaded from here.
 */
synchronized(DriverManager.class) {  
  // synchronize loading of the correct classloader.
  if(callerCL == null) {
      callerCL = Thread.currentThread().getContextClassLoader();
   }    

 
if(url == null) {
    throw new SQLException("The url cannot be null", "08001");
}
    
println("DriverManager.getConnection(\"" + url + "\")");
    
if (!initialized) {
    initialize();
}

synchronized (DriverManager.class){ 
            // use the readcopy of drivers
    drivers = readDrivers;  
        }

// Walk through the loaded drivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null;
for (int i = 0; i < drivers.size(); i++) {
    DriverInfo di = (DriverInfo)drivers.elementAt(i);
      
    // If the caller does not have permission to load the driver then 
    // skip it.
    if ( getCallerClass(callerCL, di.driverClassName ) != di.driverClass ) {
println("    skipping: " + di);
continue;
    }
    try {
println("    trying " + di);
Connection result = di.driver.connect(url, info);
if (result != null) {
    // Success!
    println("getConnection returning " + di);
    return (result);
}
    } catch (SQLException ex) {
if (reason == null) {
    reason = ex;
}
    }
}
    
// if we got here nobody could connect.
if (reason != null)    {
    println("getConnection failed: " + reason);
    throw reason;
}
    
println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
    }

这个函数代码比较多,但是我们关注的核心代码就一句:
Connection result = di.driver.connect(url, info);
其中di就是我们前面加载驱动后DriverManager的成员变量readDrivers包含的一个对象,也就是调用com.mysql.jdbc.driver的connect函数,但是从上面该类代码可知,它只包含一个构造函数和静态代码段,connect函数从何而来?
别忘了com.mysql.jdbc.driver继承自NonRegisteringDriver,这也是MySQL驱动下的一个类,进入该类,找到connect函数:
package com.mysql.jdbc;
/***省略引用和注释***/
public class NonRegisteringDriver implements java.sql.Driver {

/***省略其他函数和注释***/
public java.sql.Connection connect(String url, Properties info)
throws SQLException {
Properties props = null;

if ((props = parseURL(url, info)) == null) {
return null;
}

try {
Connection newConn = new com.mysql.jdbc.Connection(host(props),
port(props), props, database(props), url, this);

return newConn;
} catch (SQLException sqlEx) {
// Don‘t wrap SQLExceptions, throw
// them un-changed.
throw sqlEx;
} catch (Exception ex) {
throw new SQLException(Messages
.getString("NonRegisteringDriver.17") //$NON-NLS-1$
+ ex.toString()
+ Messages.getString("NonRegisteringDriver.18"), //$NON-NLS-1$
SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
}
}

}
因为NonRegisteringDriver也是java.sql.Driver的实现,返回的也是JDBC中Connection的实现,所以如上面向接口编程,即可从DriverManager中得到MySQL的Connection。
总结
JDBC的分析介绍到此结束,如果有兴趣大家可以看一下其他数据库驱动的源码,因为都是根据JDBC而来,所以大都大同小异。

时间: 2025-01-25 14:37:19

JOBC的相关文章

七,JOBC数据库编程

七,JOBC数据库编程 一,java数据库编程步骤 1,将数据库驱动包考入lib目录: 2,加载驱动--整个操作数据库程序运行期间只需要加载一次 Class.forName("com.mysql.driver");加载driver驱动 整个操作数据库程序,只需要加载一次 3,开启连接 1,开启链接调用Connection接口:(登录数据库) Connection con=null; con=DriverManager.getConnection("地址:端口/库:编码格式,

Oracle学习(11):PLSQL程序设计

PL/SQL程序结构及组成 什么是PL/SQL? ?PL/SQL(Procedure Language/SQL) ?PLSQL是Oracle对sql语言的过程化扩展 ?指在SQL命令语言中增加了过程处理语句(如分支.循环等),使SQL语言具有过程处理能力. SQL优点 交互式非过程化: 数据操纵功能强: 自动导航语句简单: 调试容易使用方便. 把SQL语言的数据操纵能力与过程语言的数据处理能力结合起来,使得PLSQL面向过程但比过程语言简单.高效.灵活和实用. 常用的结合语言 lPlsql(or

DELL服务器之系统部署环境初准备

本人习惯通过cobbler部署物理机系统,那我就来说说部署系统前需要做哪些准备 1.BIOS设置是否符合标准.比如F1/F2错误提示.系统启动顺序.是否有开启PXE启动等. 2.你从哪个网卡进行pxe引导,cobbler部署需确定对应服务器网卡的MAC地址. 3.做磁盘阵列,此项是最耗费时间的.也是这个原因,才有了这个文档. 4.更改idrac root用户的密码,并新增一个组内通用用户. 一.racadm环境部署及文档说明 此文档仅适用于dell且配置了iDRAC远程管理卡的机器(主要是配置了

Hadoop日记Day15---MapReduce新旧api的比较

我使用hadoop的是hadoop1.1.2,而很多公司也在使用hadoop0.2x版本,因此市面上的hadoop资料版本不一,为了扩充自己的知识面,MapReduce的新旧api进行了比较研究. hadoop版本1.x的包一般是mapreduce hadoop版本0.x的包一般是mapred 我们还是以单词统计为例进行研究,代码如下,如代码1.1所示: package old; import java.io.IOException; import java.net.URI; import ja

java中调用kettle作业以及生成web service 接口

第一步:(前提将kett中lib下的所有jar包拷贝到java项目lib目录)创建并连接资源库,如果只用这一个工作空间,可以将此段代码放入静态代码块,代码如下: KettleEnvironment.init(); //创建资源库对象,此时的对象还是一个空对象 KettleDatabaseRepository repository = new KettleDatabaseRepository(); //创建资源库数据库对象,类似我们在spoon里面创建资源库 //(数据库连接名称,数据库类型,连接

java 知识点随记

JAVA 读取配置文件: Properties props= new Properties();//文件在src目录下,编译会被加载到classpath下. Props.load(Test.class.getClassLoader().getResourceAsStream(‘observer.properties’); String temp = Props.getproperty(‘observers’); HTML 非表单标签: b粗体  u 下划线  i 斜体 del 删除效果 a 超链

实验二(作业调度)

#include<stdio.h>#define N 24 typedef struct node{ char name[10]; /*作业名*/ int proc; //进程优先数*/ int ArriTime;//到达时间 int StartTime;//开始时间 int NeedTime; /*进程所需时间*/ int RunTime; /*运行时间*/ int EndTime;//结束时间 int ZzTime;//周转时间 float Zznum; char state; //进程的

实验二(作业调度)FCFS

#include<stdio.h>#define N 24 typedef struct node{char name[10]; /*作业名*/int proc; //进程优先数*/ int ArriTime;//到达时间int StartTime;//开始时间int NeedTime; /*进程所需时间*/ int RunTime; /*运行时间*/int EndTime;//结束时间int ZzTime;//周转时间float Zznum;char state; //进程的状态*/ /*s

Hadoop入门(二)

一.Hadoop 1.0的模型: | split 0->map-[sort]->[1,3..] |  /merge | ==> reducer-->part 0 =>HDFS replication split 1->map-[sort]->[2,6..] |------------- | ==> reducre--->part 1         =>HDFS replication split 2->map-[sort]->[4,