再说JDBC
来源:互联网 发布:d80军刀图纸数据 编辑:程序博客网 时间:2024/06/01 08:13
上篇文章《再说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
- 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));即可在控制台中看到输出。
加载驱动
驱动使用很简单,将数据库驱动放到项目的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 loadedregisterDriver: driver[className=sun.jdbc.odbc.JdbcOdbcDriver,sun.jdbc.odbc.JdbcOdbcDriver@134e4fb]DriverManager.initialize: jdbc.drivers = nullJDBC DriverManager initializedregisterDriver: driver[className=com.mysql.jdbc.Driver,com.mysql.jdbc.Driver@157c2bd]可以看到先加载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而来,所以大都大同小异。
- 再说JDBC
- 再说Could not execute JDBC batch update
- 再说测试
- 再说分页
- 再说痰
- 再说MVC
- 开通再说
- 再说线程
- 再说Ucweb
- 再说springsecurity
- 再说RCA
- 再说python
- 再说指针
- 刷点分再说
- 再说墨水
- 再说堆
- 试试再说
- 再说mustache
- 深入理解磁盘文件系统之inode
- linux c mysql demo
- IOCP六:UDP 客户端退出
- HDU_1715 大斐波数
- Highcharts常用属性的说明
- 再说JDBC
- (新人上路)成绩单实现对象三种排序方法 及归档解归档方法
- HDOJ 题目2084 数塔(动态规划)
- air-line ticket selling system
- 如何判断用户用的是Android手机还是IOS手机
- Linux操作系统CPU信息
- unity3d,通过WWW加载图片纹理
- hql中setDate和setTimeStamp的区别
- Quartz入门实例7-处理被中断的Job