JDBC 提供了一種強大、全面的接口用來從 Java 程序訪問數(shù)據(jù)庫。對于較小的項目來說,使用 JDBC 似乎是理所當然的,它使一些程序員避免了一起使用數(shù)據(jù)庫。本文描述了一種簡單的包裝器庫,它讓使用簡單的數(shù)據(jù)庫易如反掌。您會發(fā)現(xiàn)您已經(jīng)開始想在編寫的每一個程序中都使用 JDBC。
事情發(fā)生得很突然。您正在修改一個程序,您原以為它是個小程序,不料竟發(fā)現(xiàn)它已經(jīng)迅速增長成為一個龐大的東西 ― 一個 項目― 而現(xiàn)在您已到了需要保存一些數(shù)據(jù)的時候了。
問題是,您并不是有意讓程序發(fā)展到這種程度的。 您只是不由自主地做了一小部分修改。您并沒有真正準備要存儲或裝入。而且,您的程序是個 applet,而 applet 是無法存儲或裝入的。
可以存儲或裝入它們嗎?
實際上,JDBC API 允許任何 Java 程序 ― 甚至是 applet ― 連接到關系型數(shù)據(jù)庫(RDBMS)上。 不幸的是,JDBC 對您的小程序來說可能有一點頭重腳輕了。畢竟,如果您希望做的只是存儲一點點數(shù)據(jù)的話,RDBMS 是一個強大、復雜的系統(tǒng)。
在本文中,我們將分析一個簡單的使用 JDBC 的抽象層。對于簡單的應用程序來說,它可以讓您只用幾行代碼就實現(xiàn)存儲和裝入結(jié)構化數(shù)據(jù)。它不會處理復雜的 RDBMS 使用,但那正是我們要避免的,不是嗎?
JDBC 的復雜性
JDBC 使用起來可能是一個復雜的 API。它不僅必須支持整個強大的 SQL 標準,還必須很好地隱藏不同數(shù)據(jù)庫引擎之間的區(qū)別。
JDBC 的復雜還在于關系型數(shù)據(jù)庫是基于 SQL 構建的,而 SQL 是要給人用的,而不是給程序用的。直接使用 JDBC 有點象同時用兩種語言編程。
雖然 JDBC 的這些方面并不是什么壞事,但它們確實與我們的目標 ― 快速地存儲少量數(shù)據(jù)相沖突。
簡化的抽象
我們將創(chuàng)建一個簡單的抽象層,讓您不必顧慮所有繁瑣的細節(jié)問題來直接使用 JDBC。如果您早已熟悉 JDBC 或關系型數(shù)據(jù)庫了,那您一眼看到我們的類列表應該是很熟悉的:
Database
Table
RowSet
Row
我們這里不是在作任何實質(zhì)性的事情;我們的數(shù)據(jù)模型本質(zhì)上和關系型模型是一樣的,但去掉了煩人的細節(jié)(同時去掉了強大的功能)。每一個類映射到一個基本的 RDBMS 概念上,同時也映射到一個 JDBC 類上。就是這種映射讓我們的 API 可以在保持易用性的同時保留它的相關特性。
這種 API 的設計是基于對我們的數(shù)據(jù)存儲需要的設想。如果您發(fā)現(xiàn)自己的程序需要一點不同的地方,您可以隨意地改變這種抽象以適應您的情況。 這些類可以被認為是一種簡化您工作的模式,而不是一成不變的規(guī)則。
如果您不熟悉 SQL 或者 RDBMS 技術,不必害怕。 下面的四節(jié)中,每一節(jié)都會幫助您熟悉我們的一個類,還有這些類映射到的 RDBMS 功能。
Database 類
當使用 JDBC 與數(shù)據(jù)庫建立連接時,您必須告訴 JDBC 在何處可以找到實際的數(shù)據(jù)。 因為不同的數(shù)據(jù)庫引擎有不同的訪問方法和描述這些方法的不同語法,所以有不止一種方法來指定數(shù)據(jù)源。 在 JDBC 中,統(tǒng)一資源標識符(Uniform Resource Identifier,URI)字符串是用來指定數(shù)據(jù)源的,而這個字符串的結(jié)構是依賴于數(shù)據(jù)庫的。
Database
類的主要目的就是封裝這個字符串,還有建立連接操作時可能需要的任何用戶名/密碼信息。
下面是如何創(chuàng)建一個 Database
對象的方法:
|
構造函數(shù)的第一個參數(shù)是數(shù)據(jù)源的 URI。 在這個示例中,我使用了 PostgreSQL數(shù)據(jù)庫引擎,而且在本機上訪問了一個名為 mito 的數(shù)據(jù)庫。 另外,我指定我的用戶名 mito 和一個空的密碼分別作為第二個和第三個參數(shù)。
一旦您創(chuàng)建了 Database
對象,您就可以使用它來訪問數(shù)據(jù),如我們在下一章可以看到的一樣。
Table 類
我們在 API 中對簡化的一個設想就是,當您從表的一行讀取數(shù)據(jù)時,您會得到整行的數(shù)據(jù)。換句話說,表的一行是作為讀寫單獨一塊數(shù)據(jù)的最小單位。這并不十分有效,但效率不是我們方法中所首要考慮的。
Talbe
類讓您可以讀寫這些行對象。您必須做的第一步是創(chuàng)建一個表對象,它簡單得只要知道它的名稱即可:
|
創(chuàng)建 Table
對象的操作實際上并沒有做任何事,只是讓對象記住自己的名稱。要做一些實際的事,我們就需要實際地使用這個 Table 對象了。在這里,我們從表中讀取一行。
|
注意,我們已經(jīng)指定了我們只需要那些‘id’值設定為‘101’的行。通過使用 getRow()
方法,我們假定只有一行符合這個條件。在另外的情況下,我們可能需要多個行,那樣我們就需要這樣使用 getRows()
方法:
|
在這種情況下,返回的值是一個 RowSet
,而不是一個 Row。 RowSet
就是 Row 的一個集合。
在接下來的兩節(jié)里,我們將討論 Row
和 RowSet
類。
Row 類
在我們的抽象中, Row
是在 RDBMS 中表示表中一行的名稱/值對的集合。不同于 RDBMS 值可以是不同的類型, Row
僅包含一種類型,即字符串類型。 這還是為了讓事情簡單一點 ― 我們假定您不需要字符串類型提供的任何更強大的功能。
一旦您有了一個 Row
,就很容易從其中取出一個值,正如我們在清單 1 中看見的一樣。
Row
中獲取值
|
還要注意的是 Row
是排序的,這樣您就可以通過索引來取出名稱/值的對。清單 2 中給出了這樣的一個示例。
Row
|
當然,您還可以改變 Row
中的值。這是在數(shù)據(jù)庫中更改數(shù)據(jù)所必需的 — 稍后我們會看到這一點。
RowSet 類
記住有一些查詢可以返回多個 Row
,這樣的話您就會得到一個 RowSet
。 RowSet
有一點不同于基于 Vector 的包裝器。你可以輕易地迭代 RowSet
中所有的 Row
,在清單 3 中可以看到這一點。
RowSet
|
一個完整的示例
現(xiàn)在我們已經(jīng)看過了所有的類,讓我們來看一個完整的示例吧。在清單 4 中,我們將抽取出符合特定條件的一套記錄,然后打印出它們的值。
|
如此容易!在下一節(jié)中,我們將看看怎樣向數(shù)據(jù)庫寫入數(shù)據(jù)。
修改數(shù)據(jù)
正如前面所提到的,使用我們的 API 讀寫數(shù)據(jù)是以整個 行為單位的。為了向數(shù)據(jù)庫寫入數(shù)據(jù),您必須創(chuàng)建(或修改) Row
對象,然后向數(shù)據(jù)庫寫入那個 Row
對象。
向數(shù)據(jù)庫寫入數(shù)據(jù)是通過使用 Table
中的 putRow
方法。這種方法有兩種變體:
public void putRow( Row row )
public void putRow( Row row, String conditions )
這兩種變體分別對應于 SQL 中的 INSERT
和 UPDATE
命令。
在第一個變體中,寫一行意味著將一個全新的行插入表中。
在第二個變體中,寫一行意味著修改一個現(xiàn)有的行。 conditions
參數(shù)使您能夠指定您想要修改的是哪一行(哪些行)。
讓我們來看看每種方法的一個示例。
插入一個新行
插入一個新行很簡單,因為您不必指定要修改的行(一行或多行)。您只是簡單地把行插入:
|
您可以重新創(chuàng)建一個 Row
,如清單 5 所示。
Row
|
或者,您可以修改一個以前曾經(jīng)從數(shù)據(jù)庫中讀取的一個現(xiàn)有的行,如清單 6 所示。
清單 6. 修改現(xiàn)有的Row
|
雖然通常是在插入時重新創(chuàng)建 Row
,更新時使用現(xiàn)有的 Row
,實際上您可以用任何方式來進行。
更新現(xiàn)有的行
正如前面的部分提到的,對于您如何 創(chuàng)建用來更新的 Row
是沒有限制的。 但是,通常您是使用一個剛從數(shù)據(jù)庫中讀出的 Row
。
為了詳細描述這一點,我們將使用一個示例(在該例子中我們讀出一個員工的姓名),改變這個名字,然后將更改后的結(jié)果寫回數(shù)據(jù)庫,如清單 7 所示。
清單 7. 通過修改Row
進行更新
|
注意我們必須在調(diào)用 putRow()
中指定條件。這樣才會使調(diào)用成為 更新,而不是 插入。
注意,這個調(diào)用將更新 所有符合條件的行,而不是其中的一行。
結(jié)論
在本文中,我們初步認識了一種通過 JDBC 包提供一種簡化的通往關系型數(shù)據(jù)庫接口的 API。這種抽象保留了 JDBC 接口的很多基本關系型功能,但對其進行了簡化,從而讓使用非常地方便。這種簡化是以效率為代價的,但當目標是簡單性時,這并不是一個令人驚奇的結(jié)果。