使用JDBC建立資料庫訪問程式
最後更新:2017-02-28
來源:互聯網
上載者:User
程式|建立|訪問|資料|資料庫
什麼是資料庫?
資料庫是以某種檔案結構儲存的一系列資訊表,這種檔案結構使您能夠訪問這些表、選擇表中的列、對錶進行排序以及根據各種標準選擇行。資料庫通常有多個 索引與這些表中的許多列相關聯,所以我們能儘可能快地訪問這些表。
以員工記錄為例,您可以設想一個含有員工姓名、地址、工資、扣稅以及津貼等內容的表。讓我們考慮一下這些內容可能如何組織在一起。您可以設想一個表包含員工姓名、地址和電話號碼。您希望儲存的其它資訊可能包括工資、工資範圍、上次加薪時間、下次加薪時間、員工業績評定等內容。
這些內容是否應儲存在一個表格中?幾乎可以肯定不應該如此。不同類別的員工的工資範圍可能沒有區別;這樣,您可以僅將員工類型儲存在員工記錄表中,而將工資範圍儲存在另一個表中,通過類型編號與這個表關聯。考慮以下情況:
Key Lastname SalaryType SalaryType Min Max 1 Adams 2 1 30000 45000 2 Johnson 1 2 45000 60000 3 Smyth 3 3 60000 75000 4 Tully 1 5 Wolff 2
SalaryType 列中的資料引用第二個表。我們可以想象出許多種這樣的表,如用於儲存居住城市和每個城市的稅值、健康計劃扣除金額等的表。每個表都有一個主鍵列(如上面兩個表中最左邊的列)和若干資料列。在資料庫中建立表格既是一門藝術,也是一門科學。這些表的結構由它們的範式指出。我們通常說表屬於1NF、2NF 或 3NF。
第一範式:表中的每個表元應該只有一個值(永遠不可能是一個數組)。(1NF)
第二範式:滿足 1NF,並且每一個非主鍵列完全依賴於主鍵列。這表示主鍵和該行中的剩餘表元之間是 1 對 1 的關係。(2NF)
第三範式:滿足 2NF,並且所有非主鍵列是互相獨立的。任何一個資料列中包含的值都不能從其他列的資料計算得到。(3NF) 現在,幾乎所有的資料庫都是基於“第三範式 (3NF)”建立的。這意味著通常都有相當多的表,每個表中的資訊列都相對較少。
從資料庫中擷取資料
假設我們希望產生一個包含員工及其工資範圍的表,在我們設計的一個練習中將使用這個表。這個表格不是直接存在在資料庫中,但可以通過向資料庫發出一個查詢來構建它。我們希望得到如下所示的一個表:
Name Min Max Tully $30,000.00 $45,000.00 Johnson $30,000.00 $45,000.00 Wolff $45,000.00 $60,000.00 Adams $45,000.00 $60,000.00 Smyth $60,000.00 $75,000.00
我們發現,獲得這些表的查詢形式如下所示
SELECT DISTINCTROW Employees.Name, SalaryRanges.Min, SalaryRanges.Max FROM Employees INNER JOIN SalaryRanges ON Employees.SalaryKey = SalaryRanges.SalaryKey ORDER BY SalaryRanges.Min;
這種語言稱為結構化查詢語言 (SQL),即 SQL,而且它是幾乎目前所有資料庫都可以使用的一種語言。SQL-92 標準被認為是一種基礎標準,而且已更新多次。
資料庫的種類
PC 上的資料庫,如 dBase、Borland Paradox、Microsoft Access 和 FoxBase。
資料庫伺服器:IBM DB/2、Microsoft SQL Server、 Oracle、Sybase、SQLBase 和 XDB。
所有這些資料庫產品都支援多種相對類似的 SQL 方言,因此,所有資料庫最初看起來好象可以互換。每種資料庫都有不同的效能特徵,而且每一種都有不同的使用者介面和編程介面。
ODBC
如果我們能夠以某種方式編寫不依賴於特定廠商的資料庫的代碼,並且能夠不改變自己的調用程式即可從這些資料庫中得到相同的結果,那將是一件很好的事。如果我們可以僅為所有這些資料庫編寫一些封裝,使它們具有相似的編程介面,這種對資料庫編程獨立於供應商的特性將很容易實現。
什麼是 JDBC?
JDBC 是對 ODBC API 進行的一種物件導向的封裝和重新設計,它易於學習和使用,並且它真正能夠使您編寫不依賴廠商的代碼,用以查詢和操縱資料庫。儘管它與所有 Java API 一樣,都是物件導向的,但它並不是很進階別的對象集. 除 Microsoft 之外,多數廠商都採用了 JDBC,並為其資料庫提供了 JDBC 驅動程式;這使您可輕鬆地真正編寫幾乎完全不依賴資料庫的代碼。另外,JavaSoft 和 Intersolv 已開發了一種稱為 JDBC-ODBC Bridge 的產品,可使您串連還沒有直接的 JDBC 驅動程式的資料庫。支援 JDBC 的所有資料庫必須至少可以支援 SQL-92 標準。這在很大程度上實現了跨資料庫和平台的可移植性。
安裝和使用 JDBC
JDBC 的類都被歸到 java.sql 包中,在安裝 Java JDK 1.4時會自動安裝。然而,如果您想使用 JDBC-ODBC 橋。JDBC-ODBC 驅動程式可從 Sun 的 Java 網站 (http://java.sun.com/) 輕鬆地找到並下載。在您擴充並安裝了這個驅動程式後,必須執行下列步驟:
將 \jdbc-odbc\classes; 路徑添加到您的 PATH 環境變數中。
將 \jdbc-odbc\classes; 路徑添加到您的 CLASSPATH 環境變數中。
JDBC 驅動程式的類型
Java 程式串連資料庫的方法實際上有四種:
1. JDBC-ODBC 橋和 ODBC 驅動程式 -- 在這種方式下,這是一個本地解決方案,因為 ODBC 驅動程式和橋代碼必須出現在使用者的每台機器中。從根本上說這是一個臨時解決方案。
2. 機器碼和 Java 驅動程式 -- 它用另一個本地解決方案(該平台上的 Java 可調用的機器碼)取代 ODBC 和 JDBC-ODBC 橋。
3. JDBC 網路的純 Java 驅動程式 -- 由 Java 驅動程式翻譯的 JDBC 形成傳送給伺服器的獨立協議。然後,伺服器可串連任何數量的資料庫。這種方法使您可能從客戶機 Applet 中調用伺服器,並將結果返回到您的 Applet。在這種情況下,中介軟體軟體供應商可提供伺服器。
4. 本機協議 Java 驅動程式 -- Java 驅動程式直接轉換為該資料庫的協議並進行調用。這種方法也可以通過網路使用,而且可以在 網頁瀏覽器的 Applet 中顯示結果。在這種情況下,每個資料庫廠商將提供驅動程式。
如果您希望編寫代碼來處理 PC 客戶機資料庫,如 dBase、Foxbase 或 Access,則您可能會使用第一種方法,並且擁有使用者機器上的所有代碼。更大的客戶機-伺服器資料庫產品(如 IBM 的 DB2)已提供了第 3 層級的驅動程式。
兩層模型和三層模型
當資料庫和查詢它的應用程式在同一台機器上,而且沒有伺服器代碼的幹預時,我們將產生的程式稱為兩層模型。一層是應用程式,而另一層是資料庫。在 JDBC-ODBC 橋系統中通常是這種情況。
當一個應用程式或 applet 調用伺服器,伺服器再去調用資料庫時,我們稱其為三層模型。當您調用稱為“伺服器”的程式時通常是這種情況。
編寫 JDBC 代碼訪問資料庫
用 ODBC 註冊您的資料庫
串連資料庫
所有與資料庫有關的對象和方法都在 java.sql 包中,因此在使用 JDBC 的程式中必須加入 "import java.sql.* "。 JDBC 要串連 ODBC 資料庫,您必須首先載入 JDBC-ODBC 橋驅動程式
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
該語句載入驅動程式,並建立該類的一個執行個體。然後,要串連一個特定的資料庫,您必須建立 Connect 類的一個執行個體,並使用 URL 文法串連資料庫。
String url = "jdbc:odbc:Northwind";
Connection con = DriverManager.getConnection(url);
請注意,您使用的資料庫名是您在 ODBC 設定面板中輸入的“資料來源”名稱。
URL 文法可能因資料庫類型的不同而變化極大。
jdbc:subprotocol:subname
第一組字元代表連線協定,並且始終是 jdbc。還可能有一個子協議,在此處,子協議被指定為 odbc。它規定了一類資料庫的連通性機制。如果您要串連其它機器上的資料庫伺服器,可能也要指定該機器和一個子目錄: jdbc:bark//doggie/elliott
最後,您可能要指定使用者名稱和口令,作為連接字串的一部分:
jdbc:bark//doggie/elliot;UID=GoodDog;PWD=woof
訪問MSSQL Server方法:(驅動程式需要:msutil.jar,msbase.jar,mssqlServer.jar)
DBDriver=com.microsoft.jdbc.sqlserver.SQLServerDriver URL=jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=demo username=sa password= maxcon=10 mincon=1 poolName=SkyDev
利用我們開發的資料庫類,使用方法如下:
DbObject DbO = new DbObject(new SqlServerConnectionFactory("localhost", 1433, "demo", "sa", "")); Connection con = DbO.getConnection(); //類代碼(不含串連工廠實現) package skydev.modules.data;
public final class SqlServerConnectionFactory extends ConnectionFactory { private final String dbDriver = "com.microsoft.jdbc.sqlserver.SQLServerDriver"; private String host; private int port; private String databaseName;
public SqlServerConnectionFactory() { super.setDriverName(dbDriver); }
/** * * @param host 資料庫所在的主機名稱:如"localhost" * @param port SQL伺服器啟動並執行連接埠號碼,如果使用預設值 1433,傳入一個負數即可 * @param databaseName 資料庫名稱 * @param userName 使用者名稱 * @param password 口令 */
public SqlServerConnectionFactory(String host, int port, String databaseName, String userName, String password) { this.setHost(host); this.setPort(port); this.setDatabaseName(databaseName); this.setUserName(userName); this.setPassword(password);
init(); }
private void init() { super.setDriverName(dbDriver); super.setUrl("jdbc:microsoft:sqlserver://" + host.trim() + ":" + new Integer(port).toString() + ";DatabaseName=" + databaseName.trim()); //super.setUrl("jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=demo"); } ……
//-----------------------------------------
訪問MySQL的方法:
DBDriver=com.mysql.jdbc.Driver URL=jdbc:mysql://localhost/demo username= password= maxcon=5 mincon=1 poolName=zhengmao
訪問資料庫
一旦串連到資料庫,就可以請求表名以及表列的名稱和內容等資訊,而且您可以運行 SQL 陳述式來查詢資料庫或者添加或修改其內容。可用來從資料庫中擷取資訊的對象有:
DatabaseMetaData 有關整個資料庫的資訊:表名、表的索引、資料庫產品的名稱和版本、資料庫支援的操作。
ResultSet 關於某個表的資訊或一個查詢的結果。您必須逐行訪問資料行,但是您可以任何順序訪問列。
ResultSetMetaData 有關 ResultSet 中列的名稱和類型的資訊。
儘管每個對象都有大量的方法讓您獲得資料庫元素的極為詳細的資訊,但在每個對象中都有幾種主要的方法使您可獲得資料的最重要訊息。然而,如果您希望看到比此處更多的資訊,建議您學習文檔以獲得其餘方法的說明。
ResultSet
ResultSet 對象是 JDBC 中最重要的單個對象。從本質上講,它是對一個一般寬度和未知長度的表的一種抽象。幾乎所有的方法和查詢都將資料作為 ResultSet 返回。ResultSet 包含任意數量的命名列,您可以按名稱訪問這些列。它還包含一個或多個行,您可以按順序自上而下逐一訪問。在您使用 ResultSet 之前,必須查詢它包含多少個列。此資訊儲存在 ResultSetMetaData 對象中。
//從中繼資料中獲得列數 ResultSetMetaData rsmd; rsmd = results.getMetaData(); numCols = rsmd.getColumnCount();
當您獲得一個 ResultSet 時,它正好指向第一行之前的位置。您可以使用 next() 方法得到其他每一行,當沒有更多行時,該方法會返回 false。由於從資料庫中擷取資料可能會導致錯誤,您必須始終將結果集處理語句包括在一個 try 塊中。
您可以多種形式擷取 ResultSet 中的資料,這取決於每個列中儲存的資料類型。另外,您可以按列序號或列名擷取列的內容。請注意,列序號從 1 開始,而不是從 0 開始。ResultSet 對象的一些最常用方法如下所示。
getInt(int); 將序號為 int 的列的內容作為整數返回。
getInt(String); 將名稱為 String 的列的內容作為整數返回。
getFloat(int); 將序號為 int 的列的內容作為一個 float 型數返回。
getFloat(String); 將名稱為 String 的列的內容作為 float 型數返回。
getDate(int); 將序號為 int 的列的內容作為日期返回。
getDate(String); 將名稱為 String 的列的內容作為日期返回。
next(); 將行指標移到下一行。如果沒有剩餘行,則返回 false。
Close(); 關閉結果集。
getMetaData(); 返回 ResultSetMetaData 對象。
ResultSetMetaData
您使用 getMetaData() 方法從 ResultSet 中擷取 ResultSetMetaData 對象。您可以使用此對象獲得列的數目和類型以及每一列的名稱。
getColumnCount(); 返回 ResultSet 中的列數。
getColumnName(int); 返回列序號為 int 的列名。
getColumnLabel(int); 返回此列暗含的標籤。
isCurrency(int); 如果此列包含帶有貨幣單位的一個數字,則返回 true。
isReadOnly(int); 如果此列為唯讀,則返回 true。
isAutoIncrement(int); 如果此列自動遞增,則返回 true。這類列通常為鍵,而且始終是唯讀。
getColumnType(int); 返回此列的 SQL 資料類型。這些資料類型包括
BIGINT BINARY BIT CHAR
DATE DECIMAL DOUBLE FLOAT INTEGER LONGVARBINARY LONGVARCHAR NULL NUMERIC OTHER REAL SMALLINT TIME TIMESTAMP TINYINT VARBINARY VARCHAR DatabaseMetaData
DatabaseMetaData 對象可為您提供整個資料庫的資訊。您主要用它擷取資料庫中表的名稱,以及表中列的名稱。由於不同的資料庫支援不同的 SQL 變體,因此,也有多種方法查詢資料庫支援哪些 SQL 方法。 getCatalogs() 返回該資料庫中的資訊目錄列表。使用 JDBC-ODBC Bridge 驅動程式,您可以獲得用 ODBC 註冊的資料庫列表。這很少用於 JDBC-ODBC 資料庫。
getTables(catalog, schema,tableNames, columnNames) 返回表名與 tableNames 相符而且列名與 columnNames 相符的所有表的說明。 getColumns(catalog, schema, tableNames, columnNames) 返回表名與 tableNames 相符而且列名與 columnNames 相符的所有表列說明。 getURL(); 獲得您所串連的 URL 名稱。
getDriverName(); 獲得您所串連的資料庫驅動程式的名稱。
擷取有關表的資訊
您可以使用 DataBaseMetaData 的 getTables() 方法來擷取資料庫中表的資訊。這個方法有如下 4 個 String 參數: results =dma.getTables(catalog, schema, tablemask, types[]);
其中參數的意義是:
Catalog 要在其中尋找表名的目錄名。對於 JDBC-ODBC 資料庫以及許多其他資料庫而言,可將其設定為 null。這些資料庫的目錄項實際上是它在檔案系統中的絕對路徑名稱。
Schema 要包括的資料庫“方案”。許多資料庫不支援方案,而對另一些資料庫而言,它代表資料庫擁有者的使用者名稱。一般將它設定為 null。
Tablemask 一個掩碼,用來描述您要檢索的表的名稱。如果您希望檢索所有表名,則將其設為萬用字元 %。請注意,SQL 中的萬用字元是 % 符號,而不是一般 PC 使用者的 * 符號。
types[] 這是描述您要檢索的表的類型的 String 數組。資料庫中通常包括許多用於內部處理的表,而對作為使用者的您沒什麼價值。如果它是空值,則您會得到所有這些表。如果您將其設為包含字串“TABLES”的單元素數組,您將僅獲得對使用者有用的表格。
一個簡單的 JDBC 程式
我們已經學習了 JDBC 的所有準系統,現在我們可以編寫一個簡單的程式,該程式開啟資料庫,列印它的表名以及某一表列的內容,然後對該資料庫執行查詢。此程式如下所示:
package skydevkit; import java.sql.*; public class JdbcOdbc_test { ResultSet results; ResultSetMetaData rsmd; DatabaseMetaData dma; Connection con;
public JdbcOdbc_test() throws SQLException { String url = "jdbc:odbc:Northwind"; try { //載入 JDBC-ODBC 橋驅動程式 Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); con = DriverManager.getConnection(url);//串連資料庫 dma = con.getMetaData();//擷取資料庫的中繼資料 System.out.println("Connected to:" + dma.getURL()); System.out.println("Driver " + dma.getDriverName()); } catch (Exception e) { System.out.println(e); } try { Statement stmt = con.createStatement();
results = stmt.executeQuery("select * from 客戶;"); ResultSetMetaData resultMetaData = results.getMetaData(); int cols = resultMetaData.getColumnCount(); String resultRow = ""; for (int i = 1; i < cols; i++) { resultRow += resultMetaData.getColumnName(i) + ";"; } System.out.println(resultRow); while (results.next()) { resultRow = ""; for (int i = 1; i < cols; i++) { try { resultRow += results.getString(i) + ";"; } catch (NullPointerException e) { System.out.println(e.getMessage()); } } System.out.println(resultRow); } } catch (Exception e) { System.out.println("query exception"); } finally { results.close(); } } }
關於調用SQLServer預存程序的例子:(用到了我們開發的資料庫連接類)
CREATE PROCEDURE [dbo].[sp_getStudentByName](@name char(10)) AS Select * from Students where [Name]=@name GO
DbObject DbO = new DbObject(new SqlServerConnectionFactory("localhost", 1433, "demo", "sa", "")); Connection con = DbO.getConnection(); CallableStatement pstmt = null; System.out.println("TestDB1()............"); /* try { pstmt = con.prepareCall("{call sp_getStudentById(?)}"); pstmt.setInt(1, 1); }*/ try { pstmt = con.prepareCall("{call sp_getStudentByName(?)}"); //注意參數如何傳遞 pstmt.setString(1, "Tom"); } …… 使用輸出參數:
CREATE PROCEDURE [dbo].[sp_insertStudent](@name char(10),@age int,@id int OUTPUT) AS insert into Students([Name],[Age]) values (@name,@age) select @id=@@IDENTITY GO
try { pstmt = con.prepareCall("{call sp_insertStudent(?,?,?)}"); pstmt.setString(1, "zengqingsong"); pstmt.setInt(2, 22);
pstmt.registerOutParameter(3, Types.INTEGER); pstmt.executeUpdate();
int id = pstmt.getInt(3); System.out.println(id); } 使用返回參數的例子:
CREATE PROCEDURE [dbo].[sp_insertStudent](@name char(10),@age int,@id int OUTPUT) AS insert into Students([Name],[Age]) values (@name,@age) select @id=@@IDENTITY –測試輸出參數 return 30 –測試返回30 GO
try { pstmt = con.prepareCall("{?=call sp_insertStudent(?,?,?)}"); pstmt.setString(2, "zengqingsong"); pstmt.setInt(3, 22);
pstmt.registerOutParameter(4, Types.INTEGER); pstmt.registerOutParameter(1, Types.INTEGER); int ret = pstmt.executeUpdate(); //執行影響的行數
int ret2 = pstmt.getInt(1); //返回參數(輸出參數) int id = pstmt.getInt(4); //輸出參數 System.out.println(ret); System.out.println(ret2); System.out.println(id); }