再說JDBC,mysqljdbc
上篇文章《再說Java EE》說明了一下什麼是規範,有什麼作用,這篇文章來細說一下JDBC。
JDBC
JDBC(Java Database Connection)也是Java EE中的一個規範,所謂規範是一組介面,如JDBC介面包含在java.sql及javax.sql包中,其中java.sql屬於JavaSE,javax.sql屬於JavaEE,部分如:
以上來自jdk中的src/java/sql。
因為提倡面向介面編程,所以建議僅使用JDBC規範中的類,規範與實現的關係如下:
使用 核心API JDBC中核心的API有:
- DriverManager:工廠類,用來生產Driver對象
- Driver:驅動程式對象的介面
- Connection:資料庫連接對象
- Statement:執行靜態SQL語句的介面
- Resultset:結果集對象的介面
操作流程
- 載入資料庫驅動
- 建立資料庫連接
- 執行SQL語句,得到結果集
- 對結果集進行CRUD處理
- 釋放資源
源碼分析
java.sql下有48個類,javax.sql下有45個類,展開分析不太現實,本文僅分析兩個類,DriverManager和Driver。不知大家注意過這個問題沒有,JDBC是介面,資料庫驅動是實現,那麼你編寫的項目是如何找到實現的呢?
控制台輸出
為了可以看到驅動載入過程中輸出的日誌,在載入驅動Class.forName("com.mysql.jdbc.Driver")之前,加上一句:
DriverManager.setLogWriter(new java.io.PrintWriter(System.out));
即可在控制台中看到輸出。
載入驅動
驅動使用很簡單,將資料庫驅動放到項目的lib中,在代碼中寫入:
Class.forName("com.mysql.jdbc.Driver"); 如果使用架構,如Hebernate設定檔中寫入:
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
很明顯,這兩種方式都是使用反射載入驅動程式,我們來看一下驅動程式Driver的原始碼,以mysql-connector-java-3.1.13為例:
package com.mysql.jdbc;import java.sql.SQLException;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()}} 核心代碼就是那段靜態代碼塊(static{}的意思是在類載入時執行一次,並且僅此一次),可以看到靜態代碼斷的意思是將此Driver類執行個體化後註冊到JDBC的java.sql.DriverManager類中,所以再來看一下JDBC的DriverManager.registerDriver:
/** * Registers the given driver with the <code>DriverManager</code>. * A newly-loaded driver class should call * the method <code>registerDriver</code> to make itself * known to the <code>DriverManager</code>. * * @param driver the new JDBC Driver that is to be registered with the * <code>DriverManager</code> * @exception SQLException if a database access error occurs */ public static synchronized void registerDriver(java.sql.Driver driver)throws SQLException {if (!initialized) { initialize();} DriverInfo di = new DriverInfo();di.driver = driver;di.driverClass = driver.getClass();di.driverClassName = di.driverClass.getName();// Not Required -- drivers.addElement(di);writeDrivers.addElement(di); println("registerDriver: " + di);/* update the read copy of drivers vector */readDrivers = (java.util.Vector) writeDrivers.clone(); } 即可將com.mysql.jdbc.Driver添加到DriverManager的成員變數readDrivers中,以後擷取資料庫連接需要這個變數的協助。
看上面的代碼發現,還調用了initialize(),查看initialize()的源碼看到它調用loadInitialDrivers(),這個函數的主要作用是載入JDBC預設驅動,registerDriver執行完,控制台的輸出語句為:
JdbcOdbcDriver class loadedregisterDriver: driver[className=sun.jdbc.odbc.JdbcOdbcDriver,sun.jdbc.odbc.JdbcOdbcDriver@134e4fb]DriverManager.initialize: jdbc.drivers = nullJDBC DriverManager initializedregisterDriver: driver[className=com.mysql.jdbc.Driver,com.mysql.jdbc.Driver@157c2bd]
可以看到先載入JdbcOdbcDriver,再載入我們加入的MySQL的driver。
擷取連結 載入驅動完畢後,如何擷取串連,繼續看DriverManager的getConnection():
// Worker method called by the public getConnection() methods. private static Connection getConnection(String url, java.util.Properties info, ClassLoader callerCL) throws SQLException {java.util.Vector drivers = null; /* * When callerCl is null, we should check the application's * (which is invoking this class indirectly) * classloader, so that the JDBC driver class outside rt.jar * can be loaded from here. */synchronized(DriverManager.class) { // synchronize loading of the correct classloader. if(callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } } if(url == null) { throw new SQLException("The url cannot be null", "08001");} println("DriverManager.getConnection(\"" + url + "\")"); if (!initialized) { initialize();}synchronized (DriverManager.class){ // use the readcopy of drivers drivers = readDrivers; }// Walk through the loaded drivers attempting to make a connection.// Remember the first exception that gets raised so we can reraise it.SQLException reason = null;for (int i = 0; i < drivers.size(); i++) { DriverInfo di = (DriverInfo)drivers.elementAt(i); // If the caller does not have permission to load the driver then // skip it. if ( getCallerClass(callerCL, di.driverClassName ) != di.driverClass ) {println(" skipping: " + di);continue; } try {println(" trying " + di);Connection result = di.driver.connect(url, info);if (result != null) { // Success! println("getConnection returning " + di); return (result);} } catch (SQLException ex) {if (reason == null) { reason = ex;} }} // if we got here nobody could connect.if (reason != null) { println("getConnection failed: " + reason); throw reason;} println("getConnection: no suitable driver found for "+ url);throw new SQLException("No suitable driver found for "+ url, "08001"); } 這個函數代碼比較多,但是我們關注的核心代碼就一句:
Connection result = di.driver.connect(url, info);
其中di就是我們前面載入驅動後DriverManager的成員變數readDrivers包含的一個對象,也就是調用com.mysql.jdbc.driver的connect函數,但是從上面該類代碼可知,它只包含一個建構函式和靜態程式碼片段,connect函數從何而來?
別忘了com.mysql.jdbc.driver繼承自NonRegisteringDriver,這也是MySQL驅動下的一個類,進入該類,找到connect函數:
package com.mysql.jdbc;/***省略引用和注釋***/public class NonRegisteringDriver implements java.sql.Driver {/***省略其他函數和注釋***/public java.sql.Connection connect(String url, Properties info)throws SQLException {Properties props = null;if ((props = parseURL(url, info)) == null) {return null;}try {Connection newConn = new com.mysql.jdbc.Connection(host(props),port(props), props, database(props), url, this);return newConn;} catch (SQLException sqlEx) {// Don't wrap SQLExceptions, throw// them un-changed.throw sqlEx;} catch (Exception ex) {throw new SQLException(Messages.getString("NonRegisteringDriver.17") //$NON-NLS-1$+ ex.toString()+ Messages.getString("NonRegisteringDriver.18"), //$NON-NLS-1$SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);}}}
因為NonRegisteringDriver也是java.sql.Driver的實現,返回的也是JDBC中Connection的實現,所以如上面向介面編程,即可從DriverManager中得到MySQL的Connection。
總結
JDBC的分析介紹到此結束,如果有興趣大家可以看一下其他資料庫驅動的源碼,因為都是根據JDBC而來,所以大都大同小異。
jdbc是什?
JDBC為工具/資料庫開發人員提供了一個標準的API,據此可以構建更進階的工具和介面,使資料庫開發人員能夠用純 Java API 編寫資料庫應用程式,同時,JDBC也是個商標名。 有了JDBC,向各種關係資料發送SQL語句就是一件很容易的事。換言之,有了JDBC API,就不必為訪問Sybase資料庫專門寫一個程式,為訪問Oracle資料庫又專門寫一個程式,或為訪問Informix資料庫又編寫另一個程式等等,程式員只需用JDBC API寫一個程式就夠了,它可向相應資料庫發送SQL調用。同時,將Java語言和JDBC結合起來使程式員不必為不同的平台編寫不同的應用程式,只須寫一遍程式就可以讓它在任何平台上運行,這也是Java語言"編寫一次,處處運行"的優勢。 Java資料庫連接體繫結構是用於Java應用程式串連資料庫的標準方法。JDBC對Java程式員而言是API,對實現與資料庫連接的服務提供者而言是介面模型。作為API,JDBC為程式開發提供標準的介面,並為資料庫廠商及第三方中介軟體廠商實現與資料庫的串連提供了標準方法。JDBC使用已有的SQL標準並支援與其它資料庫連接標準,如ODBC之間的橋接。JDBC實現了所有這些面向標準的目標並且具有簡單、嚴格類型定義且高效能實現的介面。 Java 具有堅固、安全、便於使用、易於理解和可從網路上自動下載等特性,是編寫資料庫應用程式的傑出語言。所需要的只是 Java應用程式與各種不同資料庫之間進行對話的方法。而 JDBC 正是作為此種用途的機制。 JDBC 擴充了 Java 的功能。例如,用 Java 和 JDBC API 發行就緒含有 applet 的網頁,而該 applet 使用的資訊可能來自遠端資料庫。企業也可以用 JDBC 通過 Intranet 將所有職員連到一個或多個內部資料庫中(即使這些職員所用的電腦有Windows、 Macintosh 和UNIX 等各種不同的作業系統)。隨著越來越多的程式員開始使用Java 程式設計語言,對從 Java 中便捷地訪問資料庫的要求也在日益增加。 MIS 管理員們都喜歡 Java 和 JDBC 的結合,因為它使資訊傳播變得容易和經濟。企業可繼續使用它們安裝好的資料庫,並能便捷地存取資訊,即使這些資訊是儲存在不同資料庫管理系統上。新程式的開發期很短。安裝和版本控制將大為簡化。程式員可只編寫一遍應用程式或只更新一次,然後將它放到伺服器上,隨後任何人就都可得到最新版本的應用程式。對於商務上的銷售資訊服務, Java 和JDBC 可為外部客戶提供擷取資訊更新的更好方法。JDBC 的用途 簡單地說,JDBC 可做三件事:與資料庫建立串連、發送 SQL 陳述式並處理結果。下列程式碼片段給出了以上三步的基本樣本: Connection con = DriverManager.getConnection("jdbc:odbc:wombat","login", "password"); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1"); while (rs.next()) { int x = rs.getInt("a"); String s = rs.getString("b"); float......餘下全文>>
jdbc串連資料庫的問題
沒有oracle的驅動嘛~~~
可能是你的war包裡面沒有把這個驅動打包進去。