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

news2025/1/12 16:12:49

对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.coloradmin.cn/o/179175.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

注解方式管理Bean

1.注解方式创建对象IOC 导入依赖 aop Component(父注解) 放在类上,用于标记,告诉spring当前类需要由容器实例化bean并放入容器中 该注解有三个子注解 Controller 用于实例化controller层bean Service 用于实例化service层bean Repository 用于实例化持久层bean 当不确定是哪一…

【刷题大本营】二叉树进阶oj题(动图讲解,附代码及题目链接)

&#x1f525;&#x1f525; 欢迎来到小林的博客&#xff01;&#xff01;       &#x1f6f0;️博客主页&#xff1a;✈️小林爱敲代码       &#x1f6f0;️欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言       这篇文章给大家带来一…

RK3399平台开发系列讲解(文件系统篇)文件回写过程介绍

🚀返回专栏总目录 文章目录 一、编程接口二、回写过程2.1、周期回写2.2、强制回写2.3、系统调用sync沉淀、分享、成长,让自己和他人都能有所收获!😄 📢进程写文件时,内核的文件系统模块把数据写到文件的页缓存,没有立即写回到存储设备。文件系统模块会定期把脏页(即…

[JavaEE]线程池

专栏简介: JavaEE从入门到进阶 题目来源: leetcode,牛客,剑指offer. 创作目标: 记录学习JavaEE学习历程 希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长. 学历代表过去,能力代表现在,学习能力代表未来! 目录: 1. 线程池是什么? 2. 线程池的实现原理 3. 标准…

Eureka集群构建步骤

目录 一、Eureka集群原理说明 二、EurekaServer集群环境构建步骤 三、将支付服务8001微服务发布到上面2台Eureka集群配置中 四、将订单服务80微服务发布到上面2台Eureka集群配置中 五、测试01 六、支付服务提供者8001集群环境构建 七、负载均衡 八、测试02 一、Eureka集…

论文投稿指南——中文核心期刊推荐(建筑科学)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

前同事居然因为 Pycharm 的这个功能,即使离职三年也依然经常被请去喝茶~

大家好&#xff0c;我是 哈士奇 &#xff0c;一位工作了十年的"技术混子"&#xff0c; 致力于为开发者赋能的UP主, 目前正在运营着 TFS_CLUB社区。 &#x1f4ac; 人生格言&#xff1a;优于别人,并不高贵,真正的高贵应该是优于过去的自己。&#x1f4ac; &#x1f4e…

教你一键生成形如Springboot的高大上banner打印效果

背景 今天闲来无聊&#xff0c;想搞一个类似于Springboot项目启动时打印效果&#xff0c;如下图&#xff1a; 问题解决方案 那这个打印效果怎么实现的呢&#xff1f; 其实&#xff0c;对于这个中效果实现起来也是很简单的。毕竟依托于Springboot强大的框架&#xff0c;任何问…

网狐大联盟非联盟成员无法创建房间解决-暂时不可创建当前游戏,请选择其他游戏!

"暂时不可创建当前游戏,请选择其他游戏!" 问题所有lua文件定位:

恶意代码分析实战 16 Shellcode分析

16.1 Lab19-01 将程序载入IDA。 一堆ecx自增的操作。到200是正常的代码段。 shellcode的解码器也是从这里开始的&#xff0c;一开始的xor用于清空ecx&#xff0c;之后将18dh赋给cx&#xff0c;jmp来到loc_21f,而在下图可以看到loc_21调用sub_208,在call指令执行后&#xff0…

40.Isaac教程--3D 物体姿态优化

3D 物体姿态优化 ISAAC教程合集地址: https://blog.csdn.net/kunhe0512/category_12163211.html 3D 物体姿态优化在操作等应用中起着至关重要的作用&#xff0c;在这些应用中&#xff0c;检测到的物体的位置会影响机器人的整体性能。 Isaac SDK 中的 3D 对象姿势优化应用程序提…

7. 好客租房-项目日常推进ing

7. 好客租房-项目日常推进ing 本章节不涉及大量内容,主要是为了推荐项目代码日常进度而设置, 包括添加mock接口, 添加更新房源接口, 为系统添加缓存. 7.1 为前端系统提供mock服务 往往在项目开发中, 为实现前后端并行开发&#xff0c;后端需要对前端所有的请求都都进行支持。…

2022年度总结——2022我在CSDN的那些事暨2023我的目标展望:Pursue freedom Realize self-worth

&#x1f4cb;前言 关于年度征文&#xff1a; 活动地址&#xff1a;2022年度征文活动页-CSDN &#x1f4da;文章目录 &#x1f4cb;前言 &#x1f3af;再见2022&#xff0c;2023新年快乐 &#x1f3af;回顾2022——“我”与我在CSDN的那些事 &#x1f9e9;伊始——CSDN&…

Allegro如何做镂空丝印操作指导

Allegro如何做镂空丝印操作指导 在PCB设计丝印的时候,会需要画镂空的丝印,Allegro升级到了172版本的时候,可以画镂空的丝印,如下图 具体操作如下 选择Shape Add Rect命令Options选择需要画到的层面,比如Silkscreen TOP层

Lesson1:走进C++的殿堂

首先在此声明一下&#xff0c;C的学习需要C语言的基础&#xff0c;不先学习C语言而直接学C是不现实的。市面上任何一本C的书籍&#xff0c;前几章的内容一定涉及到C语言的学习。 一、什么是C 照片上的这位老人便是C语言之父——本贾尼斯特劳斯特卢普&#xff08;Bjarne Stroust…

JavaScript学习

JavaScript 是一门跨平台、面向对象的脚本语言&#xff0c;而Java语言也是跨平台的、面向对象的语言&#xff0c;只不过Java是编译语言&#xff0c;是需要编译成字节码文件才能运行的&#xff1b;JavaScript是脚本语言&#xff0c;不需要编译&#xff0c;由浏览器直接解析并执行…

Spring核心模块解析—BeanDifinition。

BeanDifinition前言什么是BeanDefinition&#xff1f;为什么要有BeanDefinition&#xff1f;BeanDifinition重点源码总结前言 Spring中的BeanDifinition在Bean的实例化流程中占有着非常重要的角色&#xff0c;如果你不了解BeanDifinition的话&#xff0c;面试或者学习Bean的生…

【Leetcode每日一题】69. x 的平方根/Sqrt(x)|二分查找---day3

博主简介&#xff1a;努力学习的预备程序媛一枚~博主主页&#xff1a; 是瑶瑶子啦所属专栏: LeetCode每日一题–进击大厂 目录题目描述题目分析&#xff1a;代码分析&#xff1a;题目描述 链接: 69. x 的平方根/Sqrt(x) 给你一个非负整数 x &#xff0c;计算并返回 x 的 算术…

10+种编程语言做个计算器

用十种编程语言开发计算器应用 C语言C#&#xff08;windows桌面软件&#xff09;Swift &#xff08;ios应用&#xff09;pythonDart&#xff08;Flutter应用&#xff0c;跨平台&#xff0c;适用安卓、ios、mac、windows、web&#xff09;Java&#xff08;安卓App&#xff09;K…

【Linux】多线程同步与互斥

目录&#x1f308;前言&#x1f338;1、Linux线程同步&#x1f368;1.1、同步概念与竞态条件&#x1f367;1.2、条件变量&#x1f33a;2、条件变量相关API&#x1f368;2.1、初始化和销毁条件变量&#x1f367;2.2、阻塞等待条件满足&#x1f383;2.3、唤醒阻塞等待的条件变量&…