JAVA加載數(shù)據(jù)庫(kù)驅(qū)動(dòng)(JDBC)
前言
之前,對(duì)Class.forName("com.mysql.jdbc.Driver");
這條動(dòng)態(tài)加載JDBC驅(qū)動(dòng)感覺很疑惑,故有了這篇短文。
一、使用JDBC連接MySQL
首先,來(lái)看一下正常使用Java操縱MySql的簡(jiǎn)單代碼邏輯。
public static boolean connectionMySqlDemo() {
Connection conn = null;
try {
// 1、動(dòng)態(tài)加載mysql驅(qū)動(dòng)
Class.forName("com.mysql.jdbc.Driver");
// 2、連接數(shù)據(jù)庫(kù)
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?"
+ "user=root&password=1234&useUnicode=true&characterEncoding=UTF8");
// 3、聲明一個(gè)Statement 用來(lái)執(zhí)行sql語(yǔ)句
Statement stmt = conn.createStatement();
// 4、執(zhí)行sql語(yǔ)句
stmt.executeUpdate("create table student(no_id char(20),name varchar(20),primary key(no_id))");
int result = stmt.executeUpdate("insert into student(no_id,name) values('1','fxleyu')");
if (result > 0) {
ResultSet rs = stmt.executeQuery("select * from student");
while (rs.next()) {
System.out.println(rs.getString(1));
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 5、關(guān)閉數(shù)據(jù)庫(kù)
if (conn != null) {
try {
conn.close();
return true;
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return false;
}
在上述代碼中,動(dòng)態(tài)加載數(shù)據(jù)庫(kù)驅(qū)動(dòng)那條語(yǔ)句感覺獨(dú)立于其余代碼邏輯。感覺缺少其并無(wú)關(guān)系(當(dāng)然缺少了會(huì)在鏈接數(shù)據(jù)庫(kù)時(shí)報(bào)java.sql.SQLException: No suitable driver found for
)。
二、疑惑
上述代碼很容易理解,除了如下Class.forName("com.mysql.jdbc.Driver");
。正常理解,該語(yǔ)句只是加載把com.mysql.jdbc.Driver
加載到JVM中,沒(méi)不會(huì)產(chǎn)生特殊作用。
閱讀com.mysql.jdbc.Driver
代碼,可以發(fā)現(xiàn)其中的隱含邏輯。
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
//
// Register ourselves with the DriverManager
//
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
/**
* Construct a new driver and register it with DriverManager
*
* @throws SQLException
* if a database error occurs.
*/
public Driver() throws SQLException {
// Required for Class.forName().newInstance()
}
}
原來(lái)該類中有靜態(tài)代碼庫(kù),其加載到JVM時(shí),會(huì)執(zhí)行該靜態(tài)代碼庫(kù)。而該代碼塊會(huì)把該類的對(duì)象實(shí)例自注冊(cè)到DriverManager
中。如此第一部分中的第二步就很容易理解了。
// 2、連接數(shù)據(jù)庫(kù)
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?"
+ "user=root&password=1234&useUnicode=true&characterEncoding=UTF8");
其就是可以根據(jù)String
來(lái)獲得上述加載的驅(qū)動(dòng),從而可以正常訪問(wèn)數(shù)據(jù)庫(kù)。
三、余以為
代碼的邏輯很重要,而Class.forName("com.mysql.jdbc.Driver");
單獨(dú)來(lái)看只是一條孤立的語(yǔ)句。沒(méi)有和上下文代碼產(chǎn)生顯示的關(guān)聯(lián),這就導(dǎo)致了余在前言中的疑惑。故,使用Class.forName("com.mysql.jdbc.Driver");
來(lái)動(dòng)態(tài)注冊(cè)驅(qū)動(dòng),會(huì)對(duì)當(dāng)前代碼邏輯產(chǎn)生不利影響。
正常的邏輯,Class.forName
方法就是加載一個(gè)指定類,并對(duì)該類做一些初始化工作(使用靜態(tài)代碼庫(kù)),其不應(yīng)該做一些其它邏輯,例如動(dòng)態(tài)注冊(cè)驅(qū)動(dòng)。如果需要注冊(cè)驅(qū)動(dòng)時(shí),應(yīng)該讓用戶自己使用DriverManager.registerDriver(new com.mysql.jdbc.Driver());
來(lái)顯示注冊(cè)。這樣代碼邏輯會(huì)很清晰。
但在這里使用顯示注冊(cè)并不合適。因?yàn)楫?dāng)new com.mysql.jdbc.Driver()
的動(dòng)作中,就有類的加載過(guò)程。在該過(guò)程中,已經(jīng)把該驅(qū)動(dòng)加載到了DriverManager
的列表中。而有顯示的注冊(cè)了一次,故DriverManager
的列表會(huì)有兩個(gè)com.mysql.jdbc.Driver
實(shí)例。也就是說(shuō),MySql的jdbc并不適合用來(lái)進(jìn)行顯示加載。
當(dāng)然,也許使用Class.forName("com.mysql.jdbc.Driver");
有MySql團(tuán)隊(duì)自己的考慮,而我當(dāng)前視野并沒(méi)有看到其好處。
四、結(jié)束
Talk is cheap, show me the code. 當(dāng)對(duì)某些代碼邏輯有疑問(wèn)時(shí),不妨查看一下相關(guān)源碼,就會(huì)豁然開朗。自勉。P.S. 第三部分的余以為靈感借鑒于“余晟以為”微信公眾號(hào),很喜歡他那句“我是這么以為的,當(dāng)然你也可以那么以為”。
參考
1、使用源碼下載路徑 http://101.96.8.142/dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.40.tar.gz