时间:2021-07-01 10:21:17 帮助过:18人阅读
JDBC(Java Database Connection)也是Java EE中的一个规范,所谓规范是一组接口。如JDBC接口包括在java.sql及javax.sql包中,当中java.sql属于JavaSE,javax.sql属于JavaEE,部分例如以下图:
以上来自jdk中的src/java/sql。
由于提倡面向接口编程。所以建议仅使用JDBC规范中的类,规范与实现的关系例如以下:
如图:
java.sql下有48个类。javax.sql下有45个类,展开分析不太现实。本文仅分析两个类,DriverManager和Driver。不知大家注意过这个问题没有,JDBC是接口,数据库驱动是实现,那么你编写的项目是怎样找到实现的呢?
为了能够看到驱动载入过程中输出的日志,在载入驱动Class.forName("com.mysql.jdbc.Driver")之前,加上一句:
DriverManager.setLogWriter(new java.io.PrintWriter(System.out));就可以在控制台中看到输出。
驱动使用非常easy。将数据库驱动放到项目的lib中。在代码中写入:
Class.forName("com.mysql.jdbc.Driver"); 假设使用框架,如Hebernate配置文件里写入:
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
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,sun.jdbc.odbc.JdbcOdbcDriver@134e4fb] DriverManager.initialize: jdbc.drivers = null JDBC DriverManager initialized registerDriver: driver[className=com.mysql.jdbc.Driver,com.mysql.jdbc.Driver@157c2bd]能够看到先载入JdbcOdbcDriver,再载入我们增加的MySQL的driver。
// 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而来,所以大都大同小异。
再说JDBC
标签:程序 api 项目 ati 输出 源代码 reason src trying