解决JDBC的class.forName()问题

Dulcea ·
更新时间:2024-09-20
· 1582 次阅读

目录

环境

准备

Db2

MySQL

代码

Db2

MySQL

分析

JDBC

class.forName()

环境

Ubuntu 22.04

IntelliJ IDEA 2022.1.3

JDK 17.0.3

Db2 v11.5.0.0

MySQL Ver 8.0.30

准备 Db2

在Db2的 sample 数据库中,创建表 t1 ,并插入一些数据。如下:

➜ ~ db2 "select * from t1" C1 C2 C3 ----------- ----------- ----------- 1 444 - 2 222 - 3 333 - 3 record(s) selected. MySQL

在MySQL的 repo 数据库中,创建表 t1 ,并插入一些数据。如下:

mysql> select * from t1; +------+-------+ | c1 | c2 | +------+-------+ | 1 | 9800 | | 2 | 10200 | +------+-------+ 2 rows in set (0.00 sec) 代码

创建Maven项目 test0924

修改 pom.xml 文件,添加依赖:

...... <!-- https://mvnrepository.com/artifact/com.ibm.db2/jcc --> <dependency> <groupId>com.ibm.db2</groupId> <artifactId>jcc</artifactId> <version>11.5.7.0</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.29</version> </dependency> ......

如上,在项目添加了Db2和MySQL的JDBC驱动。

Db2

创建类 Test0924_Db2

package pkg1; import java.sql.*; public class Test0924_Db2 { public static void main(String[] args) throws ClassNotFoundException { // Class.forName("com.ibm.db2.jcc.DB2Driver"); try ( Connection connection = DriverManager.getConnection("jdbc:db2://localhost:50000/sample", "db2inst1", "passw0rd"); Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery("select * from t1"); ) { // System.out.println(connection.getClass().getName()); while (rs.next()) { System.out.println(rs.getInt(1)); } } catch (SQLException e) { throw new RuntimeException(e); } } }

运行程序,结果如下:

1
2
3

MySQL

创建类 Test0924_Mysql

package pkg1; import java.sql.*; public class Test0924_Mysql { public static void main(String[] args) throws ClassNotFoundException { // Class.forName("com.mysql.jdbc.Driver"); try ( Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/repo", "root", "123456"); Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery("select * from t1"); ) { // System.out.println(connection.getClass().getName()); while (rs.next()) { System.out.println(rs.getInt(1)); } } catch (SQLException e) { throw new RuntimeException(e); } } }

运行程序,结果如下:

1
2

分析 JDBC

比较两个Java文件可见,连接Db2和连接MySQL的方式非常类似,唯一的区别在于,调用 DriverManager.getConnection() 方法时,传入的URL不同:

Db2: jdbc:db2://localhost:50000/sample

MySQL: jdbc:mysql://localhost:3306/repo

更确切的说,只是协议的不同: db2 VS. mysql

我们知道,JDBC是一套标准,各个厂商分别有着自己的实现,也就是各自的JDBC驱动。这也就是为什么一开始,我们就先引入Db2和MySQL的JDBC驱动。

JDBC中几个重要的类:

java.sql.DriverManager

java.sql.Connection

java.sql.Statement

java.sql.ResultSet

注意: ConnectionStatementResultSet 都是需要关闭的,一种方法是在 finally 块里显式调用其 close() 方法。本例中,使用了Java 7引入的 try() 块来自动释放资源(它们都实现了 AutoCloseable 接口)。

class.forName()

以前我们学习JDBC的时候,被告知第一步要先使用 Class.forName() 方法,导入特定的JDBC驱动。

但是通过本文的两个例子,我们看到,即使省略这一步,也没有问题,DriverManager能够自动找到合适的驱动。

那么问题来了:

调用 Class.forName() 方法,到底干了什么?

为什么本文中不调用该方法也没问题?

我们知道,如果某个类之前没有被使用过,则调用 Class.forName() 方法,会做几件事情,包括实例化该类的Class对象,并且执行其static块,等等。

对于JDBC驱动,以Db2驱动为例,查看 com.ibm.db2.jcc.DB2Driver 类,可以找到如下代码:

static { DB2BaseDataSource.class.getClass(); try { registeredDriver__ = new DB2Driver(); DriverManager.registerDriver(registeredDriver__); } catch (SQLException var1) { ap.f = lr.a(b7.a(DB2Driver.class, (ds)null, ErrorKey.ERROR_REGISTER_WITH_DRIVER_MGR, "10032"), ap.f); } }

可见,调用了 DriverManager.registerDriver() 方法注册了Db2的驱动。

同理,对于MySQL,它的驱动类 com.mysql.cj.jdbc.Driver (是 com.mysql.jdbc.Driver 类的父类)里有如下代码:

static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } }

可见,类似的,也是调用了 DriverManager.registerDriver() 方法注册了MySQL的驱动。

由此,我们知道,调用 class.forName() 方法来装载驱动,其作用是注册了该驱动。

那么为什么本文中不调用方法也没问题呢?

java.sql.Connection 是一个接口,我们通过打印 connection.getClass().getName() 来看看具体的类名(参见代码中的注释部分)。

Db2: com.ibm.db2.jcc.t4.b

MySQL: com.mysql.cj.jdbc.ConnectionImpl

可见,即使不通过 class.forName() 方法来显式注册指定的驱动,而直接调用 DriverManager.getConnection() 方法,则根据传入的URL不同,也能获取正确的数据库连接。

可以去查看DriverManager的源码,大致如下:

...... for (DriverInfo aDriver : registeredDrivers) { // If the caller does not have permission to load the driver then // skip it. if (isDriverAllowed(aDriver.driver, callerCL)) { try { println(" trying " + aDriver.driver.getClass().getName()); Connection con = aDriver.driver.connect(url, info); if (con != null) { // Success! println("getConnection returning " + aDriver.driver.getClass().getName()); return (con); } } catch (SQLException ex) { if (reason == null) { reason = ex; } } ......

也就是说,它会先生成驱动的列表,然后遍历列表,根据传入的URL,尝试使用当前驱动来连接数据库,如果能连上,就OK,否则就尝试下一个驱动。

当然,如果调用 Class.forName() 方法显式注册驱动,则会把驱动类放到列表的第一个,优先使用它来连接数据库。

到此这篇关于关于JDBC的class.forName()问题的文章就介绍到这了,更多相关JDBC的class.forName()内容请搜索软件开发网以前的文章或继续浏览下面的相关文章希望大家以后多多支持软件开发网!



class jdbc

需要 登录 后方可回复, 如果你还没有账号请 注册新账号