JAVA连接数据库是其众多功能中的一部分,主要有两种方式连接DataBase: 一种是采用JDBC-ODBC桥,另一种则是称之为纯驱动连接DataBase,第一种方式在大型项目中基本上不再使用,本系列文章主要分析纯驱动源码。
对于初学JAVA者,甚至那些使用JAVA做过几年开发的程序员来讲,对于JDBC的工作原理都不一定能够明白。知其然,不知其所以然。遇到问题就不知所 措了。通过针对于MYSQL JDBC源码的分析,对于JAVA是如何连接数据库,其中到底做了些什么工作,一步步解剖开来,更好的理解JDBC。
使用JAVA连接数据库,首先要做的就是在程序中导入sql包,然后装载驱动类、获取连接、获取语句对象、发送SQL命令然后得到结果
请看以下代码片段:
package javaT.sql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class MysqlDemo {
public static void main(String[] args) throws Exception {
Connection conn = null;
String sql;
// MySQL的JDBC URL编写方式:jdbc:mysql://主机名称:连接端口/数据库的名称?参数=值
// 避免中文乱码要指定useUnicode和characterEncoding
// 执行数据库操作之前要在数据库管理系统上创建一个数据库,名字自己定,
// 下面语句之前就要先创建javademo数据库
String url = "jdbc:mysql://192.168.1.111:3306/appcenter_new?useUnicode=true&characterEncoding=utf8";
try {
// 之所以要使用下面这条语句,是因为要使用MySQL的驱动,所以我们要把它驱动起来,
// 可以通过Class.forName把它加载进去,也可以通过初始化来驱动起来,下面三种形式都可以
Class.forName("com.mysql.jdbc.Driver");// 动态加载mysql驱动
// or:
// com.mysql.jdbc.Driver driver = new com.mysql.jdbc.Driver();
// or:
// new com.mysql.jdbc.Driver();
System.out.println("成功加载MySQL驱动程序");
// 一个Connection代表一个数据库连接
conn = DriverManager.getConnection(url,"jbjava","jb98");
// Statement里面带有很多方法,比如executeUpdate可以实现插入,更新和删除等
Statement stmt = conn.createStatement();
sql = "create table student(NO char(20),name varchar(20),primary key(NO))";
int result = stmt.executeUpdate(sql);// executeUpdate语句会返回一个受影响的行数,如果返回-1就没有成功
if (result != -1) {
System.out.println("创建数据表成功");
sql = "insert into student(NO,name) values('2012001','陶伟基')";
result = stmt.executeUpdate(sql);
sql = "insert into student(NO,name) values('2012002','周小俊')";
result = stmt.executeUpdate(sql);
sql = "select * from student";
ResultSet rs = stmt.executeQuery(sql);// executeQuery会返回结果的集合,否则返回空值
System.out.println("学号\t姓名");
while (rs.next()) {
System.out
.println(rs.getString(1) + "\t" + rs.getString(2));// 入如果返回的是int类型可以用getInt()
}
}
} catch (SQLException e) {
System.out.println("MySQL操作错误");
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
conn.close();
}
}
}
此段代码有两部分,连接不同数据库时所需要装载的驱动类以及连接字符串,以此获取连接。
Class.forName()装载类,在调用一个类的构造方法,初始化静态成员或者这个类有main方法时,JVM都会装载对应的类。
首先我们就看看com.mysql.jdbc.Driver类做了什么事情,找到MYSQL-JDBC驱动源码,解压之后找到src目录,然后找到com.mysql.jdbc下的Driver.java类
Driver类继承NonRegisteringDriver 同时实现接口java.sql.Driver
此类会有一个静态块
static { try { java.sql.DriverManager.registerDriver(new Driver()); //1 } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }
Class.forName的作用是要求JVM查找并加载指定的类, 也就是说JVM装载此类并执行静态块代码
此静态块只有一句关键部分,1处
在JDBC规范中明确要求这个Driver类必须向DriverManager注册自己,即任何一个JDBC 驱动
的Driver类的代码都必须类似下面这段
java.sql.DriverManager.registerDriver(new Driver());
所以,如果你要自己实现一个数据库的JDBC驱动,那么就得实现java.sql.Driver接口,并且需要在实现类中使用 java.sql.DriverManager.registerDriver(new Driver())注册自己,new Driver()就是创建一个Driver对象,所以此类会有一个无参数的构造函数:
下面再看看DriverManager.registerDriver()这个方法,源码如下:
上一篇中分析了Class.forName(“com.mysql.jdbc.Driver”)幕后所做的事情,也就是在Driver实现类中的静 态块和构造函数,本篇主要来分析一下静态块的一句代码:DriverManager.registerDriver方法和其它相关的调用。
registerDriver方法是一个静态方法,它所要做的工作就是加载所有系统提供的驱动,并把它们添加到具体的类中,形成对象。同时还创建连接,是一个管理驱动的工具类。如果我们使用的是mysql,那么加载的也就是它的驱动。
此方法的源码如下:
一、初始化操作
1、看看1处的代码,判断是否初始化,这个判断的变量是一个静态全局boolean值,初始为false
private static boolean initialized = false;
如果此变量的值为false那么它将会进入初始化方法,源码如下:
2、Initialize方法中判断initialized值是否为真(其实就是通过此boolean变量判断是否已经初始化完成),之后设置 initialized值为true,接着又会调用另一个方法loadInitialDrivers() 同样是静态方法,用于调用系统类装载器,装载所有系统提供的驱动:
loadInitialDrivers()源码:
主要代码分析:
下面这段创建了一个内部类对象,创建此对象时,它会从系统服务中加载驱动
DriverService ds = new DriverService();
DriverService内部类的具体代码:
此句代码就是找到所有的拥有权限的java.sql.Driver的实现
java.security.AccessController.doPrivileged(ds);
下面这段,意思是得到系统属性jdbc.drivers对应驱动的驱动名称,使用了JAVA的安全许可
再看看后面的判断和循环
首先判断驱动服务对象是否为null,如果为null则返回,否则进入while循环,这个循环会依次遍历多个数据库驱动,因为jdbc:drivers 会有多个数据库驱动,驱动名是以:分割,接下来就是通过Class.forName依次装载驱动类,在其中使用了 ClassLoader.getSystemClassLoader()系统类装载器。
上面分析的就是在registerDriver方法中所要做的第一件事情:初始化。可以看到initialize()做的工作就是装载驱动,同时还 需要使用到系统的一些功能。如: java.security.AccessController.doPrivileged,此方法允许在一个类实例中的代码通知这个 AccessController,它的代码主体享受特权(Privileged),它不管这个请求是由什么代码所引发的,只是单独负责对它可得到的资源 的访问请求。比如说,一个调用者在调用doPrivileged方法时,可被标识为特权。AccessController做访问控制决策时,如果 checkPermission方法遇到一个通过doPrivileged方法调用而被视为特权调用者,那么checkPermission方法不会作许 可检查,表示那个访问请求是被允许的,如果调用者没有许可,则会抛出一个异常。
如:ClassLoader.getSystemClassLoader(),java中所有类都是通过ClassLoader装载的,ClassLoader可以为java程序提供很好的动态特性,有必要去深入理解哦。
接下来再看初始化之后的代码:
创建DriverInfo对象
DriverInfo di = new DriverInfo();
DriverInfo驱动信息类,是一个内部类,
源码如下:
此类就是添加了三个属性,分别表示驱动对象,驱动的Class对象,以及驱动的类名;同时重写了toString方法。此内部类的作用就是以可以创建DriverInfo对象,以对象的形式保存驱动信息。
接下来就是设置对象的三个属性:
然后添加到集合writeDrivers中,这个集合是Vector类型,定义为DriverManager的属性
writeDrivers定义:
private static java.util.Vector writeDrivers = new java.util.Vector();
驱动添加到集合
writeDrivers.addElement(di);
最后就是调用writeDrivers对象的clone方法
readDrivers = (java.util.Vector) writeDrivers.clone();
readDrivers也是一个类型为Vector的集合,定义为DriverManager的属性
为什么要先添加到writeDrivers然后再 clone到readDrivers中呢?
writeDrivers和 readDrivers两个都是驱动集合,无论是注册驱动抑或是取消注册,都是先对writeDrivers驱动集合中的数据进行添加或删除,然后再把 writeDrivers中的驱动都clone到readDrivers中,每次取出Driver并不是在writeDrivers中,而是在 readDrivers中取得。那么这两个驱动集合便可以这样理解,writeDrivers驱动集合负责注册驱动和注销驱动,readDrivers驱 动集合负责提供可用的驱动对象,readDrivers中的驱动对象应该都是可用的。把二者分开,使用者就不需加任何判断,很方便。
这里又涉及到一个知识就是clone, 有兴趣的朋友可以查看相关JAVA文档,Thinking in java 中也有详细描述。
这就是初始化的全过程,写了这么多,实际上只做一件事情,就是完成所有驱动的加载。装载之后就是连接了,在连载三当中我会详细描述。
第二篇中讲解了如何装载驱动,以及它的初始化过程。
本篇分析一下连接数据库时使用的获取数据库连接的代码:
DriverManager.getConnection方法有三种重载方式,这里我们使用带有三个参数的方法,第一个表示数据库的URL, 第二、三个分别
是用户名和密码。获取数据库连接如此简单,只要把三个参数准确无误的写上去,连接肯定就能获取。但是连接方法中到底给我们做
了哪些工作呢? 下面找到DriverManager类的静态方法getConnection源码一探究竟。
1处定义了一个Properties对象,java.util.Properties它是用来在文件中存储键值对的,其中键和值是用等号分隔的。可以将值以
key-value的形式存入Properties.
2处调用方法getCallerClassLoader得到类装载器对象, 这个地方有点意思,既然是调用方法,怎么变成了下面这种形式呢?根本就
不见方法体。
源码:
getCallerClassLoader()是一个静态原生方法,返回类型为ClassLoader, 被声明为native, 说明这个方法是一个原生方法,也就是说
这个方法是用C/C++语言实现的,并且被编译成了DLL,由JAVA调用。这些函数的实体在DLL中,JDK源码并不包含,在JAVA源文件中是
找不到源代码的。不同的平台其实现也有所差异。这也是JAVA的底层机制,实际上JAVA就是在不同的平台上调用不同的native方法实
现对操作系统的访问。native关键字一般是和C/C++联合开发的时候使用。如果标明为native 要求运行时通知操作系统,这个函数必
须给我实现,JAVA需要调用。如果未实现,那么调用时会抛出一个异常java.lang.UnsatisfiedLinkError
接下来判断用户名和密码,并将其存放到Properties对象中:
调用获取连接的另一个重载方法
getCollection(String url, java.util.Properties info, ClassLoader callerCL)
return (getConnection(url, info, callerCL));
getConnection(url, info, callerCL) 源码:
此方法代码较多,我们一步步来分析。
1、 传参
从前面的那个getConnection方法中形成的三个参数传递到这个getConnection方法中, 参数包括连接数据库的URL,包装了用户名和
密码的对象info, 通过调用原生函数返回的类装载器callerCL
2、 同步DriverManager.class
同步DriverManager的Class对象,synchronized同步的对象为DriverManager.class,是为同步正确的类加载器来加载类,在同步块
中判断传入的类装载器对象是否存在,如果为null, 通过当前线程来获取上下文类装载器,保证JDBC驱动程序类以外的rt.jar中的类
可以在这里被加载。有关于Thread和synchronized读者可以参考java多线程编程
3、 判断URL,如果为null则抛出SQLException异常
判断initialized的值,如果未初始化,继续调用初始化方法,此处在第二篇中已详细解释初始化过程,初始化之后writeDrivers 和readDrivers 将会有系统所有驱动数据,接下来使用synchronized同步DriverManager.class对象, 将方法中定义的集合drivers 引用 readDrivers对象,readDrivers是从writeDrivers拷贝过来
通过for循环,遍历drivers集合,其中每个元素的类型为DriverInfo, 这个在第二篇中也有详细描述
首先取得集合中的每一个对象元素,调用getCallerClass()方法
getCallerClass方法源码:
这个方法返回一个Class对象,通过指定的类装载器来装载驱动类。这个方法内做得非常小心,如果出现异常都会把需要返
回的Class对象置为null.
在if语句中调用getCallerClass方法得到的Class 对象和每一个驱动的Class对象比较,不相等的话就继续执行下一次循环
,否则都调用Driver的connect方法,传入url, 和 info,通过这个方法返回Connection连接对象
Driver接口的实现类中的connect方法具体所做的工作将在下一篇中详述
connect 方法是java.sql.Driver接口中定义的方法,如果连接的数据库不同,那么为不同的数据库编写JDBC驱动将变得很灵活,实现Driver接口 即可。连接数据库时首先得装载JDBC驱动,也就是调用 Class.forName(“com.mysql.jdbc.Driver”)方法,在第一篇中已经列出mysql jdbc Driver类的源码,此类继承NonRegisteringDriver同时实现了java.sql.Driver接口。找到 NonRegisteringDriver类会发现它也实现了java.sql.Driver接口:
public class NonRegisteringDriver implements java.sql.Driver { …… }
在getConnection方法中有如下一句代码,
Connection result = di.driver.connect(url, info);
di是DriverInfo类型的对象,此对象中包含了Driver的引用,但是在com.mysql.jdbc.Driver类中只有一个静态块和一个 构造方法,那么connect方法有可能是在其父类中实现。如前所述,父类为NonRegisteringDriver,在mysql驱动包的源码中可以 找到此类,:
com.mysql.jdbc.NonRegisteringDriver
那么di.driver.connect(url, info) 调用的connect方法就是NonRegisteringDriver类中的connect方法,源码如下:
在connect方法中传入了两个参数,一个是String 类型的url, 另一个是Properties类型的连接属性信息。
首先判断url是否为null,此判断逻辑中使用了如下类的方法
调用类com.mysql.jdbc.StringUtils
方法 startsWithIgnoreCase 其中使用了String 类中的方法regionMatches
regionMatches方法两种原型为:
传入的静态常量参数:
根据下面的判断调用不同的方法返回Connection对象
如果url为null, 则通过如下方法返回Connection
未完待续......
(选自 http://blog.csdn.net/brilliancezhou/article/details/5425655)
相关推荐
Java MySQL驱动源码是连接Java应用程序与MySQL数据库的关键组件,其主要实现是通过JDBC(Java Database Connectivity)接口。在Java中,JDBC提供了一组API,使得开发人员能够用标准的Java语言来访问和操作各种数据库...
在Java编程领域,JDBC...总之,Java通过JDBC连接MySQL提供了灵活且强大的数据访问能力,而源码包的分析则有助于开发者深入理解底层机制,提升技能水平。学习并掌握这部分内容对于任何Java开发者来说都是极其有价值的。
6. 并发和事务支持:JDBC的并发控制和事务管理机制在MySQL驱动中的实现。 通过对MySQL JDBC源码的学习,开发者能够提升对数据库操作的效率,更好地理解和解决数据库相关的性能问题,以及定制化需求。例如,你可以...
MySQL 8 数据库驱动源码分析 MySQL 是世界上最流行的开源关系型数据库管理系统之一,而 `mysql-connector-java` 是 MySQL 官方提供的用于 Java 应用程序连接到 MySQL 数据库的 JDBC 驱动程序。在 MySQL 8 版本中,...
这里提供的“mysql-connector-8.0.26.tar.gz”是 MySQL 8.0.26 版本的 Java 驱动程序,它遵循 JDBC(Java Database Connectivity)标准,使得 Java 应用能够方便地访问 MySQL 数据库。JDBC 驱动分为四种类型:Type 1...
Eclipse JDBC连接数据库源码分析 在Java编程中,JDBC(Java Database Connectivity)是用于与各种数据库进行交互的一组接口和类。本资源“eclipse-jdbc连接数据库源码”提供了一系列关于如何使用Eclipse IDE进行...
关于源码分析,MySQL Connector/J的源码可以帮助我们深入理解JDBC驱动的工作原理,包括如何建立网络连接、解析SQL语句以及处理结果集等。源码中包含了如`com.mysql.jdbc.Driver`(驱动注册类)、`...
MySQL的JDBC驱动通常为`mysql-connector-java.jar`。通过`java.sql.DriverManager`的`getConnection()`方法,你可以创建一个数据库连接。记得配置好数据库URL、用户名和密码。 2. **执行SQL操作**: 一旦建立连接...
通过分析MySQL Connector/J的源码,开发者可以更好地理解JDBC的工作机制,学习数据库驱动的实现,这对于开发高效、健壮的Java数据库应用非常有帮助。同时,这也是提升数据库性能、排查问题和定制化需求的重要途径。
在“mysql-driver:mysql jdbc驱动源码阅读”这个主题中,我们将深入探讨该驱动的工作原理、关键组件以及源码分析。 1. **JDBC驱动类型**: JDBC驱动分为四种类型:Type 1、Type 2、Type 3 和 Type 4。MySQL ...
**JDBC数据连接源码详解** Java Database Connectivity (JDBC) 是Java编程语言与各种数据库交互的一种标准接口。...通过分析和学习这些源码,你可以更好地理解和运用JDBC技术,实现高效、安全的数据库应用。
关于源码分析,MySQL JDBC驱动的源代码可以提供深入理解数据库连接的工作原理、事务处理、批处理以及错误处理机制等。对于开发者来说,阅读源码有助于优化性能,解决特定问题,或者为自定义功能提供灵感。 在工具...
综上所述,这个"Java+Swing+Mysql实现通讯录管理系统源码"项目是一个综合性的Java编程实践,涵盖了GUI设计、数据库操作和异常处理等多个核心知识点,对于学习Java桌面应用开发和数据库管理具有很高的参考价值。...
深入探讨JDBC的高级主题,包括DbUtils源码分析、Spring对JDBC的封装等,有助于更深层次地理解和掌握JDBC技术。 综上所述,JDBC不仅是Java应用程序与数据库交互的基础,而且通过其丰富的功能和API,为开发者提供了...
标题中的"JSP_JDBCconn_oracle_java_MYSQL_源码.zip"暗示了这是一个关于使用Java的JSP(JavaServer Pages)技术,通过JDBC(Java Database Connectivity)连接Oracle和MySQL数据库的源代码集合。这个压缩包可能包含...
在Java开发中,JDBC(Java Database Connectivity)是用于与关系型数据库进行交互的主要接口。在处理大量数据时,为了提高性能和用户体验,通常需要实现分页查询功能。本源码示例着重讲解如何利用JDBC进行分页查询,...
MySQL Connector/J是MySQL数据库与Java应用程序之间的桥梁,它是一个实现了JDBC(Java Database Connectivity)标准的驱动程序,使得Java开发者能够方便地连接到MySQL数据库进行数据操作。在MySQL-Connector-Java-...
这个源码包包含了实现JDBC(Java Database Connectivity)规范的MySQL驱动,使得Java应用程序能够无缝地连接到MySQL服务器。 在MySQL Connector/J 6.0.6中,我们可以深入理解以下几个关键知识点: 1. **JDBC驱动**...
MySQL JDBC,也称为MySQL Connector/J,是MySQL数据库与Java应用程序之间的桥梁,允许Java开发者通过JDBC(Java Database Connectivity)API连接并操作MySQL数据库。在"mysqljdbc src"中,我们探讨的是MySQL JDBC 5...
源码分析可以帮助我们了解数据库连接、查询执行、结果集处理等过程的具体实现,这对于优化性能、解决兼容性问题或开发自定义驱动都是很有帮助的。 总之,这个资源对于学习和使用Java与MySQL数据库交互的开发者来说...