再说JDBC

上篇文章《再说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));

就可以在控制台中看到输出。

载入驱动

驱动使用非常easy。将数据库驱动放到项目的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而来,所以大都大同小异。

时间: 2024-12-28 23:01:55

再说JDBC的相关文章

jdbc的简单使用

1.加载驱动(mysql的驱动是com.mysql.jdbc.Driver,SqlServer的驱动是 com.microsoft.sqlserver.jdbc.SQLServerDriver) 2.加载数据库的连接(url, username,password) 3.编写sql语句(String sql="select * from grade  where gradeName = ?";) 4.遍历查询结果 [while (resultSet.next()) {   System.

商城项目整理(三)JDBC增删改查

商品表的增加,修改,删除,订单表的增加,确认,用户表的查看,日志表的增加,查看 商品表建表语句: 1 create table TEST.GOODS_TABLE 2 ( 3 gid NUMBER not null, 4 gname VARCHAR2(90), 5 gdetails CLOB, 6 gpicture VARCHAR2(100), 7 gprice NUMBER, 8 gleixing NUMBER, 9 gpinpai VARCHAR2(20) 10 ) 11 tablespace

jdbc驱动jar导入eclipse

在使用JDBC编程时需要连接数据库,导入JAR包是必须的,导入其它的jar包方法同样如此,导入的方法是 打开eclipse 1.右击要导入jar包的项目,点properties 2.左边选择java build path,右边选择libraries 3.选择add External jars 4.选择jar包的按照路径下的确定后就行了. Java连接MySQL的最新驱动包下载地址 http://www.mysql.com/downloads/connector/j 有两种方法导入jar包,第一种

JDBC

Java语言访问数据库的一种规范,是一套API JDBC (Java Database Connectivity) API,即Java数据库编程接口,是一组标准的Java语言中的接口和类,使用这些接口和类,Java客户端程序可以访问各种不同类型的数据库.比如建立数据库连接.执行SQL语句进行数据的存取操作. JDBC规范采用接口和实现分离的思想设计了Java数据库编程的框架.接口包含在java.sql及javax.sql包中,其中java.sql属于JavaSE,javax.sql属于JavaE

java链接MySQL数据库时使用com.mysql.jdbc.Connection的包会出红线问题

package com.swift; //这里导入的包是java.sql.Connection而不是com.mysql.jdbc.Connection import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class TestJDBC { public static void main(String[] args) { try { Class.forName(

Tomcat JDBC Pool使用说明

Maven依赖 <dependency>   <groupId>org.apache.tomcat</groupId>   <artifactId>tomcat-jdbc</artifactId>   <version>8.5.9</version> </dependency> 最新版本为9.0,推荐使用8.5.9稳定版 常用配置 连接池配置项很多,下面只列出了推荐配置,项目组可根据自身情况进行增减 <b

tomcat启动过程报the JDBC Driver has been forcibly unregistered问题的修复过程

最近两天在整理关于flume的总结文档,没有启动过tomcat.昨天晚上部署启动,发现报了如题的错误,全文如下: 严重: The web application [/oa-deploy] registered the JBDC driver [com.microsoft.sqlserver.jdbc.SQLServerDriver] but failed to unregister it when the web application was stopped. To prevent a mem

使用JDBC如何提高访问数据库的性能?

1. 使用数据连接池(Connection Pool), 避免使用DriverManager.getConnection. 2. 合理的配置数据连接池参数,设置数据连接池的初始大小,最大连接数,连接超时时间等. 3. 选择合适的事务等级,按照不同的数据库操作类型选择不同的事务等级. 4. 及时关闭Connection,不关闭的话会严重影响系统的性能,甚至造成系统罢工. 5. 优化Statement 1) 选择合适的Statement, 根据不同的数据库操作选择Statement, Prepare

回滚的意义---JDBC事务回滚探究

JDBC手动事务提交回滚的常见写法一直是rollback写在commit的catch之后: try{ conn.setAutoCommit(false); ps.executeUpdate(); ps.executeUpdate(); conn.commit(); }catch(Exception e){ conn.rollback(); } 但是,这种回滚是没有意义的: 一旦commit前出错, 就不提交了, 回滚无用 一旦commit了, 说明没错, 不用回滚 找到一篇和我观点相同的文章:

Hive JDBC——深入浅出学Hive

第一部分:搭建Hive JDBC开发环境 搭建:Steps ?新建工程hiveTest ?导入Hive依赖的包 ?Hive  命令行启动Thrift服务 ?hive --service hiveserver & 第二部分:基本操作对象的介绍 Connection ?说明:与Hive连接的Connection对象 ?Hive 的连接 ?jdbc:hive://IP:10000/default" ?获取Connection的方法 ?DriverManager.getConnection(&q