JDBC数据库驱动及原理

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/use_admin/article/details/89416172

前言

JDBC即Java DataBase Connectivity,java数据库连接;JDBC 提供的API可以让JAVA通过API方式访问关系型数据库,执行SQL语句,获取数据;常见关系型数据库如Oracle、MySQL、SQLServer等;对于非关系型数据库如Redis、mongonDB等就显得无力;关系型数据库最典型的数据结构是表,易于维护,灵活使用(使用表结构,支持SQL语言的简单及复杂表及多表之间的查询),非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合(文档型、键值对型、列式数据库、图形数据库),相对于关系型数据库(读写性能比较差,效率低,灵活度低、高并发处理显得鸡肋);非关系型数据库优点多多,速度快、高拓展、低成本(就数据结构复杂,其他都比较简单,学习难度就有点大)。
言归正传,从根本来讲,JDBC是一种规范,它提供的接口,是一套完整的、可移植的访问底层数据库的程序。

JDBC驱动结构模型

分两种架构模型:三层和两层;下面是三层软件架构的模型,分别是表示层UI、业务逻辑层BLL、数据访问层DAL;两层架构的就把表现层UI拿掉。

JDBC驱动结构模型

java中使用JDBC连接数据库

在应用程序中进行数据库连接,调用JDBC接口,首先要将特定厂商的JDBC驱动实现加载到系统内存中,然后在应用程序中就可以直接根据URL和用户密码来识别获取对应的驱动实例,与数据库建立连接,对数据库进行相关操作。

  	//1.加载mysql数据库驱动
  	static String driverName = "com.mysql.jdbc.Driver";
  	//2.加载oracle数据库驱动
  	//static String driverName = "oracle.jdbc.driver.OracleDriver";
  	//3.加载SQL Server数据库驱动
  	//static String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
    static String url = "jdbc:mysql://127.0.0.1:3306/mysql";
    static String username = "root";
    static String password = "123456";
    public void getConnection() {
        try {
            // 1.1、加载驱动,使用Class.forName()将驱动类加载到内存
            Class.forName(driverName);
            //1.2或者使用
            //Driver driver = (Driver) Class.forName(driverName).newInstance();
            // 2.1、获取connection
            Connection conn = DriverManager.getConnection(url,username,password);
            //2.2或者使用
           /* Properties info = new Properties();
            info.put("user", username);
            info.put("password", password);
            Connection conn = driver.connect(url, info);
            */
            System.out.println(conn);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

JDBC驱动原理详解

对于已经安装好的数据库之后,我们想要直接使用数据库,比如保存用户数据资料等,要通过该数据库厂商提供的驱动程序才能与数据库交互,其实就是数据库厂商的JDBC接口实现;我们只需使用他们提供的对JDBC接口实现类的jar文件。如Oracle数据库对应使用ojdbc8.jar包等等。不同关系型数据库版本使用的JDBC的jar包都不同。
1.上面加载的JDBC驱动的方法有两种,一种使用DriverManager管理(推荐),一种直接Driver(耦合太强,不推荐)
1.1 Class.forName(driverName)和Driver driver = (Driver) Class.forName(driverName).newInstance();
因为驱动本质就是一个class,Class.forName()的作用就是将class加载到内存中,而这个driveName其实就是实现了java.sql.Driver(在java.lang.Object包下找到)接口的类(此类可以在数据库厂商提供的JDBC jar包中找到);当使用Class.forName(driverName)加载驱动类到内存中的时候,同时会执行这个驱动类中的静态代码块,创建一个Driver实例,然后调用DriverManager.registerDriver(Driver实例)进行驱动注册,供后面使用。
下面使用oracle数据库为例来源码分析:
1.1.1在oracle.jdbc.driver包下的OracleDriver驱动类,实现了java.sql.Driver类
在这里插入图片描述
1.1.2当使用Class.forName()加载驱动类oracle.jdbc.driver.OracleDriver.class的时候,会执行oracle.jdbc.driver.OracleDriver.class中的静态代码块和java.sql.DriverManager.class中的静态代码块:

//oracle.jdbc.driver.OracleDriver.class中的静态代码块
package oracle.jdbc.driver;

import java.security.AccessController;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import oracle.jdbc.OracleDatabaseMetaData;
import oracle.jdbc.driver.OracleDriver.1;
import oracle.security.pki.OracleSecretStore;
import oracle.security.pki.OracleWallet;
public class OracleDriver implements Driver {
	private static OracleDriver defaultDriver = null;
	private static final String _Copyright_2004_Oracle_All_Rights_Reserved_;
	//省略。。。	
	static {
		Timestamp localTimestamp = Timestamp.valueOf("2000-01-01 00:00:00.0");
		try {
			if (defaultDriver == null) {
			//1.不存在OracleDriver驱动实例就创建该实例--自己加的注释
				defaultDriver = new OracleDriver();
			//2.将Oracle驱动实例进行注册--自己加的注释
				DriverManager.registerDriver(defaultDriver);
			}
		} catch (RuntimeException localRuntimeException) {
			;
		} catch (SQLException localSQLException) {
			;
		}
		_Copyright_2004_Oracle_All_Rights_Reserved_ = null;
	}
}

//	java.sql.DriverManager.class中的静态代码块
package java.sql;

import java.util.Iterator;
import java.util.ServiceLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.concurrent.CopyOnWriteArrayList;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
public class DriverManager {
 // List of registered JDBC drivers
    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
    private static volatile int loginTimeout = 0;
    private static volatile java.io.PrintWriter logWriter = null;
    private static volatile java.io.PrintStream logStream = null;
    // Used in println() to synchronize logWriter
    private final static  Object logSync = new Object();
/* Prevent the DriverManager class from being instantiated. */
    private DriverManager(){}


    /**
     * Load the initial JDBC drivers by checking the System property
     * jdbc.properties and then use the {@code ServiceLoader} mechanism
     */
    static {
      //加载配置在jdbc.drivers系统变量中的驱动driver--自己的注释
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }
  private static void loadInitialDrivers() {
        String drivers;
        try {
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                    return System.getProperty("jdbc.drivers");
                }
            });
        } catch (Exception ex) {
            drivers = null;
        }
        // If the driver is packaged as a Service Provider, load it.
        // Get all the drivers through the classloader
        // exposed as a java.sql.Driver.class service.
        // ServiceLoader.load() replaces the sun.misc.Providers()

        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {

                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();

                /* Load these drivers, so that they can be instantiated.
                 * It may be the case that the driver class may not be there
                 * i.e. there may be a packaged driver with the service class
                 * as implementation of java.sql.Driver but the actual class
                 * may be missing. In that case a java.util.ServiceConfigurationError
                 * will be thrown at runtime by the VM trying to locate
                 * and load the service.
                 *
                 * Adding a try catch block to catch those runtime errors
                 * if driver not available in classpath but it's
                 * packaged as service and that service is there in classpath.
                 */
                try{
                    while(driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                } catch(Throwable t) {
                // Do nothing
                }
                return null;
            }
        });

        println("DriverManager.initialize: jdbc.drivers = " + drivers);

        if (drivers == null || drivers.equals("")) {
            return;
        }
        String[] driversList = drivers.split(":");
        println("number of Drivers:" + driversList.length);
        for (String aDriver : driversList) {
            try {
                println("DriverManager.Initialize: loading " + aDriver);
                Class.forName(aDriver, true,
                        ClassLoader.getSystemClassLoader());
            } catch (Exception ex) {
                println("DriverManager.Initialize: load failed: " + ex);
            }
        }
    }
}

当使用Class.forName(driverName)加载驱动类到内存中的时候,同时会执行这个驱动类中的静态代码块,创建一个Driver实例;然后调用DriverManager 进行注册Driver;DriverManager 作为 Driver 的管理器,它在第一次被调用的时候,它会被加载到内存中,然后执行其定义的static静态代码段,在静态代码段中,有一个 loadInitialDrivers() 静态方法,用于加载配置在jdbc.drivers 系统属性内的驱动Driver,配置在jdbc.drivers 中的驱动driver将会首先被加载上面的静态代码块使用的是DriverManager来统一管理操作Driver和获取Connection对象;DriverManager驱动管理类方便管理存在多个Driver驱动实例的情况;可以进行注册和删除加载的驱动程序,可以根据给定的Url获取符合url协议的驱动Driver并建立Connection连接,进行数据交互。
java.sql.DriverManager类的方法摘要:
在这里插入图片描述 DriverManager.registerDriver(Driver driver)的参数是一个java.sql.Driver接口的实现类实例对象,
java.sql.Driver接口的方法摘要有:
在这里插入图片描述 1.2使用DriverManager获取Connection时,使用DriverManager提供的getConnection()方法,该方法会自动根据url匹配对应的驱动Driver实例,然后调用Driver实例实现java.sql.Driver接口方法connect()获取对应的URL的数据库连接,返回Connection对象实例;如果不使用DriverManager可以直接Driver实例调用java.sql.Driver接口的connect(String url,Properties info)方法获取连接。如上面代码两者是等价的

			//加载JDBC驱动
            Class.forName(driverName);
            //获取数据库连接
            Connection conn = DriverManager.getConnection(url,username,password);

跟下面是一样的效果:

	//获取JDBC驱动
	Driver driver = (Driver) Class.forName(driverName).newInstance();
	//或者Driver driver  = DriverManager.getDriver(url);
	Properties info = new Properties();
	info.put("user", username);
	info.put("password", password);
	//获取数据库链接
	Connection conn = driver.connect(url, info);

1.3获取数据库时的认证
利用Driver的接口方法acceptsURL(String url) ,该方法只会校验url是否符合协议和是否能打开该url连接,每个数据库厂商都会制定自己的url协议,只有符合自己的协议形式的url才认为自己能够打开这个url;但不会判断url是否有效。
例如oracle定义的自己的url协议如下:
jdbc:oracle:thin:@//:/ServiceName
jdbc:oracle:thin:@::
oracle的acceptsURL(String url)方法源码如下:

package oracle.jdbc.driver;

import java.security.AccessController;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import oracle.jdbc.OracleDatabaseMetaData;
import oracle.jdbc.driver.OracleDriver.1;
import oracle.security.pki.OracleSecretStore;
import oracle.security.pki.OracleWallet;
public class OracleDriver implements Driver {
//省略。。。
	public boolean acceptsURL(String var1) {
		if (var1.startsWith("jdbc:oracle:")) {
			return this.oracleDriverExtensionTypeFromURL(var1) > -2;
		} else {
			return false;
		}
	}
	private int oracleDriverExtensionTypeFromURL(String var1) {
		int var2 = var1.indexOf(58) + 1;
		if (var2 == 0) {
			return -2;
		} else {
			int var3 = var1.indexOf(58, var2);
			if (var3 == -1) {
				return -2;
			} else if (!var1.regionMatches(true, var2, "oracle", 0, var3 - var2)) {
				return -2;
			} else {
				++var3;
				int var4 = var1.indexOf(58, var3);
				if (var4 == -1) {
					return -3;
				} else {
					String var5 = var1.substring(var3, var4);
					if (var5.equals("thin")) {
						return 0;
					} else {
						return !var5.equals("oci8") && !var5.equals("oci") ? -3 : 2;
					}
				}
			}
		}
	}
}

猜你喜欢

转载自blog.csdn.net/use_admin/article/details/89416172