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

导读:本篇文章讲解 对JDBC驱动注册–DriverManager.registerDriver和Class.forName(driverClass)的理解,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

对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;
            }
        }

    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/85501.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!