对JDBC驱动注册--DriverManager.registerDriver和Class.forName(driverClass)的理解

news/2024/5/19 0:01:41 标签: 数据库, mysql, java, JDBC, 驱动注册

JDBCDriverManagerregisterDriverClassforNamedriverClass_0">对JDBC驱动注册–DriverManager.registerDriver和Class.forName(driverClass)的理解

JDBC提供了独立于数据库的统一API,MySQL、Oracle等数据库公司都可以基于这个标准接口来进行开发。包括java.sql包下的Driver,Connection,Statement,ResultSet是JDBC提供的接口。而DriverManager是用于管理JDBC驱动的服务类,主要用于获取Connection对象(此类中全是静态方法)

当我们查看API,在Driver接口中,明确要求:Driver接口是每个驱动程序类必须实现的接口。Java SQL 框架允许多个数据库驱动程序。每个驱动程序都应该提供一个实现 Driver 接口的类。并且明确:在加载某一 Driver 类时,它应该创建自己的实例并向 DriverManager 注册该实例。这意味着用户可以通过调用以下程序加载和注册一个驱动程序Class.forName(“foo.bah.Driver”)

下边重点分析 注册驱动的四种方式

第一种:

 Driver driver = new Driver();//com.mysql.jdbc.Driver
 DriverManager.registerDriver(driver);

也即为

DriverManager.registerDriver(new Driver());

第二种:

new Driver();

第三种:

Class.forName("com.mysql.cj.jdbc.Driver");

第四种:

可以直接不写

四种注册方式有什么不同呢?

第一、二种方式,相对比较好理解,就是先创建数据库驱动然后调用registerDriver()方法完成注册

第三种方式是利用发射机制来完成的,直接看的话, 我们会想 Class.forName(driverClass) 只能帮助我们得到Driver的Class对象啊,为什么会帮我们完成注册了呢。从上边对Driver()的API的查阅,API要求:在加载某一 Driver 类时,它应该创建自己的实例并向 DriverManager 注册该实例。我们猜想是在类加载时,就自动完成了注册

第四种方式JDBC 4.0 Drivers 必须包括 META-INF/services/java.sql.Driver 文件此文件包含 java.sql.Driver 的 JDBC 驱动程序实现的名称应用程序不再需要使用 Class.forName() 显式地加载 JDBC 驱动程序

下边去具体看一下源码:

第一种方法,其JDK1.7下的DriverManger的registerDriver()方法:

 public static synchronized void registerDriver(java.sql.Driver driver)
        throws SQLException {
 
        /* Register the driver if it has not already been added to our list */
        if(driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver));
        } else {
            // This is for compatibility with the original DriverManager
            throw new NullPointerException();
        }
 
        println("registerDriver: " + driver);
 
    

 // List of registered JDBC drivers
    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<DriverInfo>();

从其源码,可以看到DriverManger将要注册的驱动程序信息封装到了DriverInfo中,然后放进了一个List中。在后边获得连接时会再用到。

第一种方法,其JDK1.8下的DriverManger的registerDriver()方法:

 public static void registerDriver(Driver driver) throws SQLException {
        registerDriver(driver, (DriverAction)null);
    }
 public static void registerDriver(Driver driver, DriverAction da) throws SQLException {
        if (driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
            println("registerDriver: " + driver);
        } else {
            throw new NullPointerException();
        }
    }
  private static final CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList();

第三种方法:第三种方法是怎么通过只要获得Driver的Class对象就可以完成注册呢,下边看一下其com.mysql.jdbc.Driver的源码:

   public class Driver extends NonRegisteringDriver implements java.sql.Driver {  
        // ~ Static fields/initializers  
        // ---------------------------------------------  
      
        //  
        // Register ourselves with the DriverManager  
        //  
        static {  
            try {  
                java.sql.DriverManager.registerDriver(new Driver());  
            } catch (SQLException E) {  
                throw new RuntimeException("Can't register driver!");  
            }  
        }  


从上边可以看到,它是用静态代码块实现的。

根据类加载机制,当执行 Class.forName(driverClass) 获取其Class对象时, com.mysql.jdbc.Driver 就会被JVM加载,连接,并进行初始化,初始化就会执行静态代码块,也就会执行下边这句代码:java.sql.DriverManager.registerDriver(new Driver());这就和第一种方式相同了。

实际上Class.forName(driverClass)中driverClass里面的内容是Driver类的全限定类名,如下图所示:在这里插入图片描述

第四种方法:可以直接不写。
在这里插入图片描述

JDBC 4.0 Drivers 必须包括 META-INF/services/java.sql.Driver 文件此文件包含 java.sql.Driver 的 JDBC 驱动程序实现的名称

应用程序不再需要使用 Class.forName() 显式地加载 JDBC 驱动程序

对于上边的四种驱动注册方法,我们一般采用第三种方法
(1)第一、二种方式 Driver driver = new Driver()在内部也执行静态代码块,这相当于实例化了两个Driver对象

(2)第一、二种方式 Driver driver = new Driver() 会产生对某一种数据库的依赖(会import驱动包),耦合性较高。

所以一般采用第三种方式

JDBC连接数据库的步骤为:
案例代码:

package _01编写第一个JDBC程序;

import com.mysql.cj.jdbc.Driver;
import java.sql.*;
/**
 * 编写第一个JDBC程序
 */
public class LoadDriverByThreeWays {
    public static void main(String[] args) {
        ResultSet resultSet = null;
        Statement statement = null;
        Connection connection = null;
        try {
            // 1. 加载驱动


            //第一种方式
//            DriverManager.registerDriver(new Driver());
            //第二种方式
//            new Driver();
            //第三种方式
//            Class.forName("com.mysql.cj.jdbc.Driver");
            //第四种方式:直接不写
            /*
             *  JDBC 4.0 Drivers 必须包括 META-INF/services/java.sql.Driver 文件。此文件包含 java.sql.Driver 的 JDBC 驱动程序实现的名称。
             * 应用程序不再需要使用 Class.forName() 显式地加载 JDBC 驱动程序。
             *
             */

            // 2. 创建连接
            /*
             * JDBC连接数据库的url格式是:
             *   jdbc:子协议://subname
             *
             * JDBC连接MySQL数据库的url格式是:
             *    jdbc:mysql://主机名:端口号/数据库名 -----> jdbc:mysql://localhost:3306/db02
             * 如果主机名:端口号是 localhost:3306 那么主机名:端口号可以省略  -----> jdbc:mysql:///db02
             */
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/powernode_jdbc", "root", "root");
            // 3. 获取Statement语句对象
            statement = connection.createStatement();
            // 4. 执行SQL语句
            String sql = "select id,name as xxx,age from student";
            resultSet = statement.executeQuery(sql);
            // 5. 解析ResultSet结果集
            /*
             * java.sql.SQLException: Before start of result set
             * 出现该异常的原因是:目前ResultSet对象具有指向其当前数据行的光标,光标位于第一行数据之前
             *
             */
            while (resultSet.next()) {
                /*
                 * resultSet中获取数据的方法getXXXX()都有两个重载方法。
                 * 一个是根据投影字段的索引获取数据;一个是根据投影字段的名称(别名)获取数据
                 *
                 * 注意:数据库索引从1开始
                 */
                // 根据投影字段的名称(别名)获取数据
                String name = resultSet.getString("xxx");
                /*
                 * 根据投影字段的索引获取数据  -- 不推荐使用
                 * 弊端:就是投影字段的顺序调整后,获取数据就就不对了
                        */
//               String name = resultSet.getString(2);
                System.out.println("name= " + name);
            }
        } catch (Exception throwables) {
            throwables.printStackTrace();
        } finally {
            // 6. 释放资源
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
                resultSet = null;
            }

            if (statement != null) {
                try {
                    statement.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
                statement = null;
            }

            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
                connection = null;
            }
        }

    }
}


http://www.niftyadmin.cn/n/30102.html

相关文章

力扣(LeetCode)393. UTF-8 编码验证(2023.01.26)

给定一个表示数据的整数数组 data &#xff0c;返回它是否为有效的 UTF-8 编码。 UTF-8 中的一个字符可能的长度为 1 到 4 字节&#xff0c;遵循以下的规则&#xff1a; 对于 1 字节 的字符&#xff0c;字节的第一位设为 0 &#xff0c;后面 7 位为这个符号的 unicode 码。 对…

Linux产生死锁的必要条件和常见的锁种类

目录前言产生死锁的4个必要条件预防(解决)死锁Linux常见的锁互斥锁(普通锁)自旋锁互斥锁和自旋锁小结递归锁读写锁乐观锁与悲观锁乐观锁和悲观锁小结其他锁(了解)前言 之前面试的时候&#xff0c;有面试官问我产生死锁的(4个)必要条件&#xff0c;这个我之前有了解过&#xff…

边缘检测、Padding、stride、三维卷积

目录1.边缘检测(edge detection)当我们做物体识别的时候&#xff0c;一般神经网络的前几层会进行边缘检测&#xff0c;然后检测到物体的一部分&#xff0c;最后检测到整个物体。边缘检测例子&#xff1a;垂直边缘检测器&#xff1a;中间的一个3x3的矩阵&#xff0c;我们称之为过…

设计模式之组合模式

什么是组合模式 组合模式是指组合多个对象形成树形结构以表示具有"整体-部分"关系的层次结构。组合模式对单个对象和组合对象的使用具有一致性。     组合模式包括以下几个角色&#xff1a;         Component(抽象构件)&#xff1a;可以是接口或者抽象类…

筑基三层 —— 青蛙跳台阶、字符串长度问题

目录 一.修炼必备 二.青蛙跳台阶问题 三.求解第n个斐波那契数 四.求解字符串长度的三种方式 一.修炼必备 1.入门必备&#xff1a;VS2019社区版&#xff0c;下载地址&#xff1a;Visual Studio 较旧的下载 - 2019、2017、2015 和以前的版本 (microsoft.com) 2.趁手武器&…

【数据结构】7.3 树表的查找

文章目录7.3.1 二叉排序树1. 二叉排序树的定义2. 二叉排序树的查找二叉排序树算法二叉排序树算法分析3. 二叉排序树的插入4. 二叉排序树的生成5. 二叉排序树的删除7.3.2 平衡二叉树1. 平衡二叉树的定义2. 平衡二叉树的平衡调整方法LL型调整RR型调整LR型调整RL型调整3. 构造平衡…

Groovy实现热部署

Groovy实现热部署一、概述二、准备工作2.1 规则接口IRule三、非Spring环境Groovy文件方式3.1 Groovy文件3.2 读取并生成实例3.3 使用这个实现四、数据库Groovy脚本方式4.1 Groovy脚本4.2 读取并生成实例五、Spring中使用Groovy的方式5.1 Groovy文件5.2 读取并生成实例5.3 使用这…

【数据结构】单调栈、单调队列

单调栈 单调栈 单调 栈 模拟单调递增栈的操作&#xff1a; 如果栈为 空 或者栈顶元素 大于 入栈元素&#xff0c;则入栈&#xff1b;否则&#xff0c;入栈则会破坏栈内元素的单调性&#xff0c;则需要将不满足条 件的栈顶元素全部弹出后&#xff0c;将入栈元素入栈。 单调…