Heim > Datenbank > MySQL-Tutorial > Hauptteil

再说JDBC

WBOY
Freigeben: 2016-06-07 15:59:24
Original
1020 Leute haben es durchsucht

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

上篇文章《再说Java EE》说明了一下什么是规范,有什么作用,这篇文章来细说一下JDBC。

JDBC

JDBC(Java Database Connection)也是Java EE中的一个规范,所谓规范是一组接口,如JDBC接口包含在java.sql及javax.sql包中,其中java.sql属于JavaSE,javax.sql属于JavaEE,部分如下图:

\

以上来自jdk中的src/java/sql。

因为提倡面向接口编程,所以建议仅使用JDBC规范中的类,规范与实现的关系如下:

\

使用

核心API

JDBC中核心的API有: DriverManager:工厂类,用来生产Driver对象Driver:驱动程序对象的接口Connection:数据库连接对象Statement:执行静态的SQL语句的接口Resultset:结果集对象的接口

操作流程

加载数据库驱动创建数据库连接执行SQL语句,得到结果集对结果集进行CRUD处理释放资源

如图:

\

源码分析

java.sql下有48个类,javax.sql下有45个类,展开分析不太现实,本文仅分析两个类,DriverManager和Driver。不知大家注意过这个问题没有,JDBC是接口,数据库驱动是实现,那么你编写的项目是如何找到实现的呢?

控制台输出

为了可以看到驱动加载过程中输出的日志,在加载驱动Class.forName("com.mysql.jdbc.Driver")之前,加上一句:

    DriverManager.setLogWriter(new java.io.PrintWriter(System.out));
Nach dem Login kopieren
即可在控制台中看到输出。

加载驱动

驱动使用很简单,将数据库驱动放到项目的lib中,在代码中写入:

    Class.forName("com.mysql.jdbc.Driver");
Nach dem Login kopieren
如果使用框架,如Hebernate配置文件中写入:
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
Nach dem Login kopieren
很明显,这两种方式都是使用反射加载驱动程序,我们来看一下驱动程序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&#39;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()
	}
}
Nach dem Login kopieren
核心代码就是那段静态代码块(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();

    }
Nach dem Login kopieren
即可将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]
Nach dem Login kopieren
可以看到先加载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&#39;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");
    }
Nach dem Login kopieren
这个函数代码比较多,但是我们关注的核心代码就一句:
    Connection result = di.driver.connect(url, info);
Nach dem Login kopieren

其中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&#39;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);
		}
	}

}
Nach dem Login kopieren

因为NonRegisteringDriver也是java.sql.Driver的实现,返回的也是JDBC中Connection的实现,所以如上面向接口编程,即可从DriverManager中得到MySQL的Connection。

总结

JDBC的分析介绍到此结束,如果有兴趣大家可以看一下其他数据库驱动的源码,因为都是根据JDBC而来,所以大都大同小异。

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage