2012年9月26日 星期三

利用 JDBC-ODBC 驅動程式來存取資料庫

MySQL Server 簡介

The materials presented in this web page is provided as is and is used solely for educational purpose. Use at your own risks.
Written by: 國立中興大學資管系呂瑞麟 Eric Jui-Lin Lu

請勿轉貼
看其他教材

本文假設你已經安裝了 MySQL Server 5.1.x 版,而且你也依據之前的說明, 建立了使用者 jlu,而且你也為 jlu 建立了一個資料庫 eric,並且在資料庫中, 建立了表格 Product。在下列的步驟中,我們將說明如何利用 JDBC 驅動程式 來開發 Java 程式以便於對表格 Product 進行增、刪、改、查的動作。 以下文件,我們假設讀者熟析 Java 程式;如果讀者想學習 Java 的物件導向 設計的技巧,我們建議購買作者所寫的 呂瑞麟與陳宜惠著,Java 101: 物件導向程式設計,上奇出版,09/2008 以及其 勘誤表
  1. 為了讓 Java 程式(包含 JSP、Java Servlets 等)能夠存取資料庫, 昇陽(Sun) 定義了 JDBC 的介面,而各家的資料庫管理系統可以依據這些介面定義, 開發出其資料庫系統的 JDBC 驅動程式。JDBC 驅動程式又分成四大類,其細節 可以參考 資料處理入門,在本文中,我們只針對 JDBC-ODBC 驅動程式來說明。
  2. JDBC-ODBC 驅動程式:若要使用 JDBC-ODBC 驅動程式,請安裝 MySQL Connector/ODBC。作者下載的是 5.1.x 版的 ZIP Archive,檔案名稱為 mysql-connector-odbc-noinstall-5.1.x-win32.zip。將檔案解壓縮之後, 請在解壓縮的目錄內,執行 install.bat 即可。請注意,如果你的開發環境 不是 Windows,那麼你的作業環境必須安裝以下驅動程式之一:unixODBC, Apple iODBC, 或者 iODBC。 安裝 JDBC-ODBC 驅動程式之後,你需要在控制台上設定 ODBC 的資訊,這些 資訊包含連結資料庫的電腦在哪裡(IP 或者主機名稱)、資料庫名稱、帳號/密碼、 以及代表以上資訊的一個名稱,該名稱為 Data Source Name(資料來源名稱; 或簡稱 dsn)。你如果 使用的是非 Windows 的 ODBC 驅動程式,則請依據該程式的設定方式進行。 首先,在 Windows XP 的環境下,請利用控制台 --> 效能及維護 --> 系統管理工具 --> 資料來源 (ODBC),並開啟"資料來源",開啟後畫面如下:
    然後,請在"系統資料來源名稱"上點一下,其畫面如下:
    請在"新增"按鈕上點一下,螢幕上會出現"建立新資料來源"的對話視窗,將可以 選擇的驅動程式清單往下拉,你會看到如以下畫面的 "MySQL ODBC 5.1 Driver", 請選擇它,並點一下"完成"按鈕。
    在點選完"完成"按鈕後,螢幕會出現一個 MySQL Connector/ODBC 的設定畫面。

    以這個範例來說,dsn 的名稱為 csie,因此請在第一個欄位填入 csie。 第二個欄位(Description)是用來描述該 dsn 的;如果你建立了許多的 dsn, 這個欄位的資料可以提醒你它的作用為何,在這個範例中,我們不填入任何資料。 第三個欄位 Server 指的是 MySQL 的所在位置,它可以是遠端的 Server 或者是當地端的 localhost;因為我們都假設在同一步電腦上執行這些 動作,所以這個欄位我們可以填入 127.0.0.1 或者 localhost。 第四個欄位 Port 指的是 MySQL Server 的連線端口號,由於我們並未修改 MySQL 的設定,而 MySQL 的預設端口號就是 3306,因此這個部分不必修改; 如果你認為使用標準端口號會造成比較大的網路安全漏洞,你可以在伺服器端 和 dsn 同時修改連線端口號。
    第五、六、七個欄位分別是使用者帳號(User)、密碼(Password)、以及 資料庫名稱(Database);依據之前的設定,我們可以分別填入 jlu、newpasswd、 以及 eric。完成後的畫面如下:

    填寫完以上資料之後,你可以點一下 "Test" 按鈕在測試一下設定是否正確,能不能 完成連線等。如果一切沒問題,最後我們必須對資料庫連線的編碼方式進行設定, 設定的方式是在 "Details>>" 按鈕點一下,然後依照以下畫面,在 "Misc Options" 上點一下,然後在 "Character Set:" 內選擇 "big5"。

    設定完成並點選完 "OK" 按鈕後,以下的畫面會出現,我們也完成了全部的設定, 這時就可以把該視窗關閉。

  3. 程式開發:一般來說,以 Java 語言(含 JSP 和 servlet)來連結 資料庫,大多需要完成以下的步驟:
    1. 利用 Class.forName(驅動程式的名稱); 載入 JDBC 的驅動程式;以 JDBC-ODBC 驅動程式的名稱為例,載入的用法為 Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");。每一種驅動程式都有其相對應的名稱,開發人員在使用之前必須 先搞清楚。
    2. 利用 Connection conn = DriverManager.getConnection(資料庫的位置, 帳號名稱, 密碼); 來產生一個 Java 程式和資料庫之間的連線(也就是 Connection 物件 conn)。 DriverManager.getConnection() 內有三個參數,第一個參數說明程式想要 跟哪一個資料庫連線;同樣的,這個參數的值會因為使用的 JDBC 驅動程式不同而 不同,以我們的範例為例,因為我們使用 JDBC-ODBC 驅動程式,而經由 ODBC 產生 資料庫連線必須藉助"資料來源名稱"(DSN);因為我們之前設定了一個名為 csie 的 DSN,所以第一個參數值必須為 "jdbc:odbc:csie"(其中 jdbc:odbc: 是 固定不變的,最後一個會隨著之前設定的 DSN 名稱改變而變動)。第二個以及 第三個參數分別為連結該資料庫所需要的"帳號名稱"以及"密碼"。
    3. 連線完成之後,我們可以開始執行 SQL 的指令了。執行 SQL 指令的方式是先 借由 conn 來產生一個 Statement 的物件,然後再藉由 Statement 的物件來執行 SQL 指令。產生 Statement 物件的方式 為 Statement aStatement = conn.createStatement();,其中 aStatement 即為 Statement 物件的名稱。
    4. SQL 指令主要執行"增、刪、改、查"四個動作,而這四個動作中只有"查詢" 會回傳一個表格的資料,其他三種都只回傳一個代表執行是否成功的整數。
      1. 如果 SQL 指令執行"增、刪、改",執行的方式為 aStatement.executeUpdate(SQL指令);,該方法回傳總共被改變的資料筆數。以在 Product 新增一筆編號 5 的產品為例,我們的程式碼即為 aStatement.executeUpdate("insert into Product values(5,'鍵盤',14.5,2)");;由於只有一筆資料被新增,所以執行後,aStatement.executeUpdate() 會回傳 1。
      2. 如果 SQL 指令執行"查詢",執行的方式為 aStatement.executeQuery(SQL指令);,該方法回傳查詢的結果;由於 SQL 查詢的結果也是一個表格,該表格由 Java 的 ResultSet 物件所代表。以查詢整個 Product 的資料為例,我們的程式碼即為 ResultSet rs = aStatement.executeQuery("select * from Product");
    5. 如果 SQL 指令是查詢,程式大多會進一步處理該查詢結果,也就是 ResultSet 物件。一個 ResultSet 物件包含兩種資料,一種是該回傳表格的 Metadata(由 ResultSetMetaData 物件所代表;該物件包含總共有幾個欄位、欄位的名稱、 欄位的資料型態等資料),另一種是表格的資料。
      1. 我們可以經由 ResultSetMetaData rsmeta = rs.getMetaData(); 來 取得 ResultSetMetaData 物件;然後經由 int cols = rsmeta.getColumnCount(); 來取得回傳表格的總欄位數;在得到總欄位數 cols 之後,我們就可以經由一個 簡單的 for 迴圈,將每一個欄位的名稱以及資料型態,經由 rsmeta.getColumnLabel(i) 以及 rsmeta.getColumnType(i)
      2. 經由 ResultSet 物件 rs 可以取得實際的查詢資料。在預設的情形下,一開始 rs 指向資料的第 0 筆,我們可以經由 rs.next() 來依序取得下一筆的資料, 一旦下一筆資料不存在,rs.next() 會回傳 false。當 rs.next() 指向某一筆資料的 時候,我們可以利用之前 rsmeta 來取得總欄位數,然後利用 rs.getString(i) 將一個一個欄位的資料以字串的方式取出。除了 getString(i) 的方式之外, 我們也可以利用 getDate(i)getTime(i)getDouble(i)getInt(i) 等方法分別取出資料型態為 Date、Time、double、int 的資料。
    6. 最後,但也是很重要的:在程式結束以前,我們必須將相關的資料庫資源釋放 出來。資源釋放的方式是經由呼叫該物件的 close() 方法達成;若釋放 Statement 物件,則與該 Statement 物件相關的 ResultSet 物件也會被釋放。 另外,在程式的最後,我們也必須經由 conn.close(); 將連線釋放。




    完成了以上的安裝以及設定之後,我們就可以開發 Java 程式來 存取資料庫的資料。我們在下列程式中為 Product 新增一筆編號 5 的產品, 新增之後,我們接著修改它、並檢查該資料是否被正確的修改;最後,再把 編號 5 的產品資料刪除掉。 這個範例程式還蠻簡單的,比較不一樣的地方,我們都加上註解了。

    import java.sql.*;
    
    public class NewODBC {
      // 試著將以下的設定以 properties 的檔案讀進來
    
      // 設定 ODBC-JDBC 驅動程式的名稱
      static String classname = "sun.jdbc.odbc.JdbcOdbcDriver";
    
      // ODBC's DNS 設為 csie,你也可以設成其他名稱
      static String jdbcURL = "jdbc:odbc:csie";
      static String UID = "jlu";
      static String PWD = "newpasswd";
      static Connection conn = null;
    
      public static void main( String argv[] ) {
        // 說明這個程式的使用方式為執行:java NewODBC Product
        if(argv.length != 1) {
          System.out.println("Usage: java NewODBC Product");
          System.exit(2);
        }
    
        // 查詢用的 SQL 字串
        String aQuery = "select * from " + argv[0];
    
        // 新增資料的 SQL 字串
        String iSQL = "insert into " + argv[0] + " values(5,'鍵盤',14.5,2)";
    
        // 修改資料的 SQL 字串
        String uSQL = "update " + argv[0] + " set Name='無線鍵盤' where ID=5";
    
        // 刪除資料的 SQL 字串
        String dSQL = "delete from " + argv[0] + " where ID=5";
    
        try {
          // 載入 JDBC-ODBC 驅動程式
          Class.forName(classname);
    
          // 連結資料庫(藉由 dns、帳號、密碼)
          conn = DriverManager.getConnection(jdbcURL,UID,PWD);
    
          // 顯示目前表格的內容
          System.out.println("Display current content");
          ShowResults(aQuery);
    
          // 新增資料並顯示目前表格的內容
          System.out.println("\nInserting a new record .....");
          InsertNew(iSQL);
          ShowResults(aQuery);
    
          // 修改資料並顯示目前表格的內容
          System.out.println("\nUpdateing a record .....");
          UpdateNew(uSQL);
          ShowResults(aQuery);
    
          // 刪除資料並顯示目前表格的內容
          System.out.println("\nDeleting a record .....");
          DeleteNew(dSQL);
          ShowResults(aQuery);
    
          conn.close();
        } catch (Exception sqle) {
          System.out.println(sqle);
          System.exit(1);
        }
      }
    
      private static void DeleteNew(String dSQL) {
        try {
          Statement aStatement = conn.createStatement();
          aStatement.executeUpdate(dSQL);
        } catch (Exception e) {
          System.out.println("Delete Error: " + e);
          System.exit(1);
        }
      }
    
      private static void UpdateNew(String uSQL) {
        try {
          Statement aStatement = conn.createStatement();
          aStatement.executeUpdate(uSQL);
        } catch (Exception e) {
          System.out.println("Update Error: " + e);
          System.exit(1);
        }
      }
    
      private static void InsertNew(String iSQL) {
        try {
          Statement aStatement = conn.createStatement();
    
          // 新增、修改、刪除都跟查詢一樣,在執行 SQL 字串前,必須先
          // 產生 statement 物件。但是跟查詢不一樣的地方在於,真正
          // 執行的方法用的是 executeUpdate(),而不是 executeQuery()
          aStatement.executeUpdate(iSQL);
        } catch (Exception e) {
          System.out.println("Insert Error: " + e);
          System.exit(1);
        }
      }
    
    
      private static void ShowResults(String aQuery) {
        try {
          // 產生一個 SQL statement 物件並利用之前建立的連線 conn
          // 送給 MySQL,然後在伺服器端執行該 statement 物件中的字串
          Statement aStatement = conn.createStatement();
          ResultSet rs = aStatement.executeQuery(aQuery);
    
          // 執行 SQL 字串後的結果被置放於一個 ResultSet 的物件
          // 依據 SQL 的原理,回傳的結果其實也是一個表格(table)
          // 因此 ResultSet 的物件包含該表格的 Metadata 以及資料
    
          // 表格的 Medata 可以經由 ResultSet 物件的 getMetaData 的
          // 方法取得,而取得的 Metadata 由一個 ResultSetMetaData 物件代表
          ResultSetMetaData rsmeta = rs.getMetaData();
    
          // 由 getColumnCount() 可以知道表格共有幾個欄位
          int cols = rsmeta.getColumnCount();
    
          // 經由 for 迴圈可以得出每一個欄位的名稱
          for(int i=1; i<=cols; i++)
          {
            if(i > 1) System.out.print("\t");
    
            // 由 getColumnLabel(i) 可以取得第 i 個欄位名稱
            System.out.print(rsmeta.getColumnLabel(i));
          }
          System.out.print("\n");
    
          // 經由 while 迴圈可以將資料一筆一筆取出來
          // rs.next() 是查詢是否還有下一筆;若有,為 true;反之,為 false
          while(rs.next()) {
            for(int i=1; i<=cols; i++)
            {
              if (i > 1) System.out.print("\t");
    
              // rs.getString(i) 取得第 i 欄的資料並以字串的方式回傳
              System.out.print(rs.getString(i));
            }
            System.out.print("\n");
          }
    
          // 將 rs 以及 statement 清掉。這個動作很重要,如果不清除的話,
          // 在資料量很大的情形下,Java 垃圾處理器來不及處理的話,就會出現異常
          aStatement.close();
        }
        // a better exception handling can be used here.
        catch (Exception e) {
          System.out.println("Exception Occurs.");
        }
      }
    }
    
















Written by: 國立中興大學資管系呂瑞麟 Eric Jui-Lin Lu


沒有留言:

張貼留言