解決JDBC的class.forName()問題
環(huán)境
- 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
數(shù)據(jù)庫中,創(chuàng)建表 t1
,并插入一些數(shù)據(jù)。如下:
? ~ db2 "select * from t1" C1 C2 C3 ----------- ----------- ----------- 1 444 - 2 222 - 3 333 - 3 record(s) selected.
MySQL
在MySQL的 repo
數(shù)據(jù)庫中,創(chuàng)建表 t1
,并插入一些數(shù)據(jù)。如下:
mysql> select * from t1; +------+-------+ | c1 | c2 | +------+-------+ | 1 | 9800 | | 2 | 10200 | +------+-------+ 2 rows in set (0.00 sec)
代碼
創(chuàng)建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驅(qū)動。
Db2
創(chuàng)建類 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); } } }
運行程序,結(jié)果如下:
1
2
3
MySQL
創(chuàng)建類 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); } } }
運行程序,結(jié)果如下:
1
2
分析
JDBC
比較兩個Java文件可見,連接Db2和連接MySQL的方式非常類似,唯一的區(qū)別在于,調(diào)用 DriverManager.getConnection()
方法時,傳入的URL不同:
- Db2:
jdbc:db2://localhost:50000/sample
- MySQL:
jdbc:mysql://localhost:3306/repo
更確切的說,只是協(xié)議的不同: db2
VS. mysql
。
我們知道,JDBC是一套標準,各個廠商分別有著自己的實現(xiàn),也就是各自的JDBC驅(qū)動。這也就是為什么一開始,我們就先引入Db2和MySQL的JDBC驅(qū)動。
JDBC中幾個重要的類:
java.sql.DriverManager
java.sql.Connection
java.sql.Statement
java.sql.ResultSet
注意: Connection
、 Statement
、 ResultSet
都是需要關(guān)閉的,一種方法是在 finally
塊里顯式調(diào)用其 close()
方法。本例中,使用了Java 7引入的 try()
塊來自動釋放資源(它們都實現(xiàn)了 AutoCloseable
接口)。
class.forName()
以前我們學(xué)習(xí)JDBC的時候,被告知第一步要先使用 Class.forName()
方法,導(dǎo)入特定的JDBC驅(qū)動。
但是通過本文的兩個例子,我們看到,即使省略這一步,也沒有問題,DriverManager能夠自動找到合適的驅(qū)動。
那么問題來了:
- 調(diào)用
Class.forName()
方法,到底干了什么? - 為什么本文中不調(diào)用該方法也沒問題?
我們知道,如果某個類之前沒有被使用過,則調(diào)用 Class.forName()
方法,會做幾件事情,包括實例化該類的Class對象,并且執(zhí)行其static塊,等等。
對于JDBC驅(qū)動,以Db2驅(qū)動為例,查看 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); } }
可見,調(diào)用了 DriverManager.registerDriver()
方法注冊了Db2的驅(qū)動。
同理,對于MySQL,它的驅(qū)動類 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!"); } }
可見,類似的,也是調(diào)用了 DriverManager.registerDriver()
方法注冊了MySQL的驅(qū)動。
由此,我們知道,調(diào)用 class.forName()
方法來裝載驅(qū)動,其作用是注冊了該驅(qū)動。
那么為什么本文中不調(diào)用方法也沒問題呢?
java.sql.Connection
是一個接口,我們通過打印 connection.getClass().getName()
來看看具體的類名(參見代碼中的注釋部分)。
- Db2:
com.ibm.db2.jcc.t4.b
- MySQL:
com.mysql.cj.jdbc.ConnectionImpl
可見,即使不通過 class.forName()
方法來顯式注冊指定的驅(qū)動,而直接調(diào)用 DriverManager.getConnection()
方法,則根據(jù)傳入的URL不同,也能獲取正確的數(shù)據(jù)庫連接。
可以去查看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; } } ......
也就是說,它會先生成驅(qū)動的列表,然后遍歷列表,根據(jù)傳入的URL,嘗試使用當(dāng)前驅(qū)動來連接數(shù)據(jù)庫,如果能連上,就OK,否則就嘗試下一個驅(qū)動。
當(dāng)然,如果調(diào)用 Class.forName()
方法顯式注冊驅(qū)動,則會把驅(qū)動類放到列表的第一個,優(yōu)先使用它來連接數(shù)據(jù)庫。
到此這篇關(guān)于關(guān)于JDBC的class.forName()問題的文章就介紹到這了,更多相關(guān)JDBC的class.forName()內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解析SQL Server 視圖、數(shù)據(jù)庫快照
在程序開發(fā)過程中,任何一個項目都離不開數(shù)據(jù)庫,這篇文章給大家詳細介紹SQL Server 視圖、數(shù)據(jù)庫快照相關(guān)內(nèi)容,需要的朋友可以參考下2015-08-08Can''t connect to local MySQL through socket ''/tmp/mysql.so
今天小編就為大家分享一篇關(guān)于Can't connect to local MySQL through socket '/tmp/mysql.sock'解決方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03使用MySQL進行千萬級別數(shù)據(jù)查詢的技巧分享
這篇文章主要介紹了如何使用MySQL進行千萬級別數(shù)據(jù)查詢的技巧,文中通過代碼示例給大家講解的非常詳細,對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-03-03CentOS7.x?安裝mysql5.7?XtraBackUp備份工具使用命令詳解
這篇文章主要介紹了CentOS7.x?安裝mysql5.7?XtraBackUp備份工具使用,本文給大家介紹了mysql安裝過程及命令使用方法,需要的朋友可以參考下2022-04-04安裝MySQL phpMyAdmin cpolar實現(xiàn)遠程訪問連接的操作步驟
這篇文章主要給大家介紹了安裝 MySQL phpMyAdmin cpolar實現(xiàn)遠程訪問連接的流程步驟,文中有詳細的圖文介紹,具有一定的參考價值,需要的朋友可以參考下2023-08-08