天天看點

J2EE 應用程式中的資料管理和資料持久性

http://kingreturn.blogchina.com/1528658.html

本文分析了在 Java 平台上可用的兩個資料管理政策:Java 對象序列化和 Java 資料庫

連接配接(JDBC)。盡管本質上這兩種資料管理政策并不存在孰優孰劣的問題,但在管理企

業資訊系統時,JDBC 輕而易舉地得以勝出。在本文中,Java 開發人員 G.V.B. Subrah

manyam 和 Shankar Itchapurapu 對序列化和 JDBC都進行了介紹,并通過讨論和執行個體來

向您展示了 JDBC 是您的最佳選擇的原因。

當您正在建立企業資訊系統時,需要確定以某種有效的方式存儲、檢索和顯示企業資料

。對于所有業務而言,資料都是獨一無二的最大資産。所有軟體系統都涉及資料,是以

,資料的重要性是無論如何強調都不過分的。

應用程式的資料管理功能包括四個基本操作,通常也需要對企業資料執行這四個操作,

它們是:建立、檢索、更新 和 删除(即 CRUD)。管理在企業系統的資料涉及在很長一

段時間範圍之内,始終如一地、成功地執行 CRUD 操作,而不必頻繁地更改實際執行這

些操作的代碼。換句話說,管理資料意味着開發穩健的、可擴充和可維護的軟體系統,

以確定成功地進行 CRUD 操作,在軟體的生命期中能夠以一緻的方式執行操作。

本文讨論了 J2EE 中的兩種可用資料管理政策:Java 對象序列化和 Java 資料庫連接配接(

JDBC)。我們将檢視這兩種方法的優缺點。這兩種資料管理政策實質上不存在孰優孰劣

。在特定實作中,政策的可用性取決于項目的範圍(出現在系統環境中的活動的活動範

圍),系統的上下文(驅動系統/子系統運作時的值的集合),以及其他的外部因素。然

而,Java 序列化并不适合于企業系統,其資料需要用一種定義良好的結構(如RDBMS)

來組織。我們首先将快速浏覽 Java 對象序列化,然後檢視 JDBC 更重要的一些方面,

進而了解後者是如何實作前者所缺乏的一些關鍵特性的。

本文并不打算對 Java 對象序列化或者 JDBC 進行全面介紹。有關這兩項技術的更多信

息,請回顧參考資料小節。

Java 對象序列化

對象序列化是最簡單的 Java 持久性政策。對象序列化是一個将對象圖平面化為一個字

節的線性序列的過程。對象圖是作為對象繼承、關聯和聚合的結果而實作的一些關系式

。對象的非暫态執行個體屬性以位元組的形式被寫入到持久存儲中。執行個體屬性的值就是執行時

間序列化時記憶體中的值。如果一個 Java 對象是可序列化的,那麼它至少必須實作 jav

a.io.Serializable 接口,該接口具有如下所示的結構:

package java.io;

public interface Serializable

{}

您可以看到,java.io.Serializable 接口并沒有聲明任何方法。它是一個記号或者标記

接口。它告訴 Java 運作時環境,該實作類是可序列化的。清單 1 顯示實作該接口的一

個示例類。

清單 1. MySerializableObject.java

import java.io.Serializable;

public class MySerializableObject extends MySuperClass implements Serializab

le

{

private String property1 = null;

private String property2 = null;

public String getProperty1()

{

return property1;

}

public void setProperty1(String val)

{

property1 = val;

}

public String getProperty2()

{

return property2;

}

public void setProperty2(String val)

{

property2 = val;

}

private void writeObject(ObjectOutputStream out)

throws IOException

{

out.writeObject (getProperty1 ());

out.writeObject (getProperty2 ());

}

private void readObject (ObjectInputStream in)

throws IOException, ClassNotFoundException

{

setProperty1 ((String) in.readObject ());

setProperty2 ((String) in.readObject ());

}

}

無需自己實作 writeObject(...) 和 readObject(...) 方法來執行序列化;Java 運作

時環境具有使這些方法可用的預設實作。然而,您可以重寫這些方法,提供如何存儲對

象狀态的您自己的實作。

關于序列化,您需要記住一些要點。首先,在序列化期間,整個對象圖(即,所有父類

和被引用類)都将被序列化。其次, Serializable 類的所有執行個體變量自身都應該是可

序列化的,除非已經特别聲明它們為暫态,或者已經重寫 writeObject(...) 和 readO

bject(...) 來隻序列化那些可序列化的執行個體變量。如果違反了後一規則,在運作時将出

現一個異常。

每個後續 J2SE 版本都對對象序列化系統進行少量的增加。J2SE 1.4 也相應地向 Obje

ctOutputStream and ObjectInputStream 增加 writeUnshared() and readUnshared()

方法。通常,一個序列化的流隻包含任何給定對象的一個序列化執行個體,并且共享對該對

象引用的其他對象可以對它進行後向引用。通常期望序列化一個對象獨立于其他對象可

能維護的任何引用。非共享的讀寫方法允許對象作為新的、獨一無二的對象被序列化,

進而獲得一個類似于對象克隆但開銷更少的效果。

Java 對象序列化存在的問題

序列化涉及到将對象圖從記憶體具體化到持久存儲(例如硬碟)中。這涉及到大量 I/O 開

銷。通常,對應用程式而言,序列化并不是最佳選擇:

管理幾十萬兆位元組的存儲資料

頻繁地更新可序列化對象

對存儲企業資料而言,序列化是一個錯誤選擇,因為:

序列化的位元組流隻對 Java 語言是可讀的。這是一個重大缺陷,因為企業系統通常是異

構的,許多應用程式要與其他應用程式共同處理相同的資料。

對象檢索涉及大量的 I/O 開銷。

沒有一個用來從序列化對象圖中檢索擷取資料的查詢語言。

序列化沒有内置的安全機制。

序列化本身并不提供任何事務控制機制,是以不能在那些需要并發通路進而不使用輔助

API 的應用程式中使用它。

Java 資料庫連接配接(JDBC)

Java 資料庫連接配接(JDBC)是一個标準的 API,它使用 Java 程式設計語言與資料庫進行互動

。諸如 JDBC 的調用級接口是程式設計接口,它們允許從外部通路 SQL 指令來處理和更新數

據庫中的資料。通過提供與資料庫連接配接的庫例程,它們允許将 SQL 調用內建到通用的編

程環境中。特别是,JDBC 有一個使接口變得極其簡單和直覺的例程的豐富收集。

在下面幾個小節中,我們将檢視通過 JDBC 與資料庫連接配接所涉及的一些步驟。我們将特

别關注與 Java 對象序列化相比,JDBC 是如何成為一個企業資料管理政策的。

建立一個資料庫連接配接

在利用 JDBC 做任何其他事情之前,需要從驅動程式供應商那裡擷取資料庫驅動程式,

并且将該庫添加到類路徑中。一旦完這項工作,就可以在 Java 程式中使用類似于下面

所示的代碼來實作實際的連接配接。

Class.forName(<your driver class Name>

J2EE 應用程式中的資料管理和資料持久性

;

Java.sql.Connection conn = DriverManager.getConnection(<connection URL>

J2EE 應用程式中的資料管理和資料持久性

;

Java 對象序列化并不需要這個該步驟,因為使用序列化來執行持久性操作并不需要 DB

MS。 序列化是一個基于檔案的機制;是以,在序列化一個對象之前,需要在目标檔案系

統中打開一個 I/O 流。

建立 JDBC Statement 和 PreparedStatement

可以用 JDBC Statement 對象将 SQL 語句發送到資料庫管理系統(DBMS),并且不應該

将該對象與 SQL 語句混淆。 JDBC Statement 對象是與打開連接配接有關聯,而不是與任何

單獨的 SQL 語句有關聯。可以将 JDBC Statement 對象看作是位于連接配接上的一個通道,

将一個或多個(您請求執行的)SQL 語句傳送給 DBMS。

為了建立 Statement 對象,您需要一個活動的連接配接。通過使用我們前面所建立的 Conn

ection 對象 con——下面的代碼來完成這項工作。

Statement stmt = con.createStatement();

到目前為止,我們已經有了一個 Statement 對象,但是還沒有将對象傳遞到 DBMS 的

SQL 語句。

當資料庫接收到語句時,資料庫引擎首先會分析該語句并查找句法錯誤。一旦完成對語

句的分析,資料庫就必須計算出執行它的最有效方法。在計算上,這可能非常昂貴。數

據庫會檢查哪些索引可以提供幫助,如果存在這樣的索引的話,或者檢查是否應該完全

讀取表中的所有行。資料庫針對資料進行統計,找出最佳的執行方式。一旦建立好查詢

計劃,資料庫引擎就可以執行它。

生成這樣一個計劃會占用 CPU 資源。理想情況是,如果我們兩次發送相同的語句到資料

庫,那麼我們希望資料庫重用第一個語句的通路計劃,我們可以使用 PreparedStateme

nt 對象來獲得這種效果。

這裡有一個主要的特性是,将 PreparedStatement 與其超類 Statement 差別開來:與

Statement 不同,在建立 PreparedStatement 時,會提供一個 SQL 語句。然後了立即

将它發送給 DBMS,在那裡編譯該語句。因而, PreparedStatement 實際上是作為一 個

通道與連接配接和被編譯的 SQL 語句相關聯的。

那麼,它的優勢是什麼呢?如果需要多次使用相同的查詢或者不同參數的類似查詢,那

麼利用 PreparedStatement,語句,隻需被 DBMS 編譯和優化一次即可。與使用正常的

Statement 相比,每次使用相同的 SQL 語句都需要重新編譯一次。

還可以通過 Connection 方法建立PreparedStatement 。下面代碼顯示了如何建立一個

帶有三個輸入參數的參數化了的 SQL 語句。

PreparedStatement prepareUpdatePrice = con.prepareStatement(

"UPDATE Sells SET price = ? WHERE bar = ? AND beer = ?");

注意,Java 序列化不支援類似于 SQL 的查詢語言。使用 Java 序列化通路對象屬性的

惟一途徑就是反序列化該對象,并調用該對象上的 getter/accessor 方法。反序列化一

個完整的對象在計算上可能很昂貴,尤其是在程式的生命期中,應用程式需要重複執行

它。

在執行 PreparedStatement 之前,需要向參數提供值。通過調用 PreparedStatement

中定義的 setXXX() 方法可以實作它。最常使用的方法是 setInt(),setFloat(),set

Double(),以及 setString()。每次執行已準備的聲明之前,都需要設定這些值。

執行語句和查詢

執行 JDBC 中的 SQL 語句的方式是根據 SQL 語句的目的而變化的。DDL(資料定義語言

)語句(例如表建立和表更改語句)和更新表内容的語句都是通過使用 executeUpdate

() 執行的。清單 2 中包含 executeUpdate() 語句的執行個體。

清單 2. 實際運作中的 executeUpdate()

Statement stmt = con.createStatement();

stmt.executeUpdate("CREATE TABLE Sells " +

"(bar VARCHAR2(40), beer VARCHAR2(40), price REAL)" );

stmt.executeUpdate("INSERT INTO Sells " +

"VALUES ('Bar Of Foo', 'BudLite', 2.00)" );

String sqlString = "CREATE TABLE Bars " +

"(name VARCHAR2(40), address VARCHAR2(80), license INT)" ;

stmt.executeUpdate(sqlString);

我們将通過先前插入的參數值(如上所示)執行 PreparedStatement ,然後在這之上調

用 executeUpdate(),如下所示:

int n = prepareUpdatePrice.executeUpdate() ;

相比之下,查詢期望傳回一個行作為它的結果,并且并不改變資料庫的狀态。這裡有一

個稱為 executeQuery() 的相對應的方法,它的傳回值是 ResultSet 對象,如清單 3

所示。

清單 3. 執行一個查詢

String bar, beer ;

float price ;

ResultSet rs = stmt.executeQuery("SELECT * FROM Sells");

while ( rs.next() ) {

bar = rs.getString("bar");

beer = rs.getString("beer");

price = rs.getFloat("price");

System.out.println(bar + " sells " + beer + " for " + price + " Dollar

s.");

}

由于查詢而産生的行集包含在變量 rs 中,該變量是 ResultSet 的一個執行個體。集合對于

我們來說并沒有太大用處,除非我們可以通路每一個行以及每一個行中的屬性。Result

Set 提供了一個光标,可以用它依次通路每一個行。光标最初被設定在正好位于第一行

之前的位置。每個方法調用都會導緻光标向下一行移動,如果該行存在,則傳回 true,

或者如果沒有剩餘的行,則傳回 false。

我們可以使用适當類型的 getXXX() 來檢索某一個行的屬性。在前面的執行個體中,我們使

用 getString() 和 getFloat() 方法來通路列值。注意,我們提供了其值被期望用作方

法的參數的列的名稱;我們可以指定用列号來代替列名。檢索到的第一列的列号為 1,

第二列為 2,依次類推。

在使用 PreparedStatement 時,可以通過先前插入的參數值來執行查詢,然後對它調用

executeQuery(),如下所示:

ResultSet rs = prepareUpdatePrice.executeQuery() ;

關于通路 ResultSet 的注釋

JDBC 還提供一系列發現您在結果集中的位置的方法:getRow(),isFirst(),isBefore

First(),isLast(),以及 isAfterLast()。

這裡還有一些使可滾動光标能夠自由通路結果集中的任意行的方法。在預設情況下,光

标隻向前滾動,并且是隻讀的。在為 Connection 建立 Statement 時,可以将 Result

Set 的類型更改為更為靈活的可滾動或可更新模型,如下所示:

Statement stmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, Resu

ltSet.CONCUR_READ_ONLY);

ResultSet rs = stmt.executeQuery("SELECT * FROM Sells");

不同的類型選項: TYPE_FORWARD_ONLY 、 TYPE_SCROLL_INSENSITIVE 和 TYPE_SCROLL

_SENSITIVE 。可以通過使用 CONCUR_READ_ONLY 和 CONCUR_UPDATABLE 選項來選擇光标

是隻讀的還是可更新的。對于預設光标,可以使用 rs.next() 向前滾動它。對于可滾動

的光标,您有更多的選項,如下所示:

rs.absolute(3); // moves to the third retrieved row

rs.previous(); // moves back one row in the retrieved result

set

rs.relative(2); // moves forward two rows in the retrieved re

sult set

rs.relative(-3); // moves back three rows in the retrieved res

ult set

對于可滾動光标的工作方式,這裡有更多的較長的描述。盡管可滾動光标對于特定應用程

序是有用的,但是它導緻極大的性能損失,是以應該限制和謹慎使用。可以在 參考資料

小節中找到關于可滾動 ResultSet 的更多資訊。

在序列化中不存在與 JDBC 的 ResultSet 相對應的機制。序列化和 JDBC 觀察底層的數

據的角度不同。JDBC (通常)假定底層資料是關系型結構的;而序列化假定底層資料是

一個對象圖。兩種技術的底層資料結構存在顯著差異。JDBC 的 Set 結構并不能自然地

映射到序列化的對象圖結構,反之亦然。當通過使用序列化語義将一個 Java 對象持久

化時,資料的底層結構變成了一個位元組流,該位元組流展示了已經序列化了的核心對象的

各種内部對象之間的關聯。

JDBC 中的 ResultSet 導航是從一個 Set 元素移動到其他元素的過程,而在對象序列化

中,這是不可能的,因為序列化涉及到對象關聯,而不是将一組行封裝到一個實體集合

中。是以,Java 對象序列化無法向您提供用這種方式通路資料單獨某個部分的能力。

事務

JDBC 允許将 SQL 語句組合到單獨一個事務中。是以,我們可以通過使用 JDBC 事務特

性來確定 ACID 屬性。

Connection 對象執行事務控制。當建立連接配接時,在預設情況下,連接配接是自動送出模式下

。這意味着每個 SQL 語句自身都被看作是一個事務,并且一完成執行就會被送出。

可以用以下方法開啟或關閉自動送出模式:

con.setAutoCommit(false) ;

con.setAutoCommit(true) ;

一旦關閉了自動送出,除非通過調用 commit() 顯式地告訴它送出語句,否則無法送出

SQL 語句(即,資料庫将不會被持久地更新)。在送出之前的任何時間,我們都可以調

用 rollback() 復原事務,并恢複最近的送出值(在嘗試更新之前)。

我們還可以設定期望的事務隔離等級。例如,我們可以将設定事務隔離等級為 TRANSAC

TION_READ_COMMITTED,這使得在送出值之前,不允許對它進行通路。并且禁止髒讀。在

Connection 接口中為隔離等級提供了五個這樣的值。預設情況下,隔離等級是可序列

化的。JDBC 允許我們發現資料庫所設定的是什麼事務隔離等級(使用 Connection 的

getTransactionIsolation() 方法)以及設定适當的等級(使用 Connection 的 setTr

ansactionIsolation() 方法)。

復原通常與 Java 語言的異常處理能力結合在一起使用。這種結合為處理資料完整性提

供一個簡單高效的機制。在下一節中,我們将研究如何使用 JDBC 進行錯誤處理。

注意,Java 對象序列化并不直接支援事務管理。如果您正在使用序列化,則将需要借助

其他的 API,例如 JTA,來獲得這個效果。然而,為了獲得事務隔離的效果,可以選擇

在執行一個更新操作時同步該序列化對象,如下所示:

Synchronized(my_deserialized_object) {

//Perform the updates etc...

}

利用異常處理錯誤

軟體程式中總是出現一些錯誤。通常,資料庫程式是關鍵性應用程式,而且适當地捕獲

和處理錯誤是有必要的。程式應該恢複并且讓資料庫處于某種一緻的狀态下。将復原與

Java 異常處理程式結合使用是達到這種要求的一種簡便方法。

通路伺服器(資料庫)的客戶(程式)需要能夠識别從伺服器傳回的所有錯誤。JDBC 通

過提供兩種等級的錯誤條件來通路這種資訊:SQLException 和 SQLWarning。SQLExcep

tion 是 Java 異常,它(如果未被處理)将會終止該應用程式。SQLWarning 是 SQLEx

ception 的子類,但是它們代表的是非緻命錯誤或意想不到的條件,是以,可以忽略它

們。

在 Java 代碼中,希望抛出異常或者警告的語句包含于 try 塊中。如果在 try 塊中的

語句抛出異常或者警告,那麼可以在對應的某個 catch 語句中捕獲它。每個捕獲語句都

指出了它準備捕獲的異常。

換句話說,如果資料類型是正确的,但是資料庫大小超出其空間限制并且不能建立一個

新表,則可能會抛出一個異常。 可以從 Connection,Statement,以及 ResultSet 對

象中擷取 SQLWarning。每個對象都隻是存儲最近 SQLWarning。是以,如果通過 State

ment 對象執行其他語句,則将放棄所有早期的警告。清單 4 舉例說明了 SQLWarning

的使用。

清單 4. 實際運作中的 SQLWarnings

ResultSet rs = stmt.executeQuery("SELECT bar FROM Sells") ;

SQLWarning warn = stmt.getWarnings() ;

if (warn != null)

System.out.println("Message: " + warn.getMessage()) ;

SQLWarning warning = rs.getWarnings() ;

if (warning != null)

warning = warning.getNextWarning() ;

if (warning != null)

System.out.println("Message: " + warn.getMessage()) ;

實際上,SQLWarning 在某種程度上比 SQLException 更為罕見。最常見的是 DataTrun

cation 警告,它表示在從資料庫讀或寫資料時存在問題。

Java 并沒有提供序列化所使用的特定的異常類。使用序列化時發生的大多數異常都與執

行的 I/O 操作有關,是以,在這些情況中 I/O 異常類将滿足要求。

批處理

JDBC 2.0 提供一個用于批處理的強大API。批處理允許積累一組 SQL 語句,并且将它們

一起發送并處理。一個典型的批處理就是銀行應用程式,該應用程式每隔一刻鐘就要更

新許多賬号。在減少從 Java 代碼到資料庫的往返次數方面,批處理是一個強大功能。

Statement 接口提供 addBatch(String) 方法,将 SQL 語句添加到一個批進行中。一旦

已經将所有的 SQL 語句都增加到該批進行中,就可以使用 executeBatch() 方法一起執

行它們。

然後,用executeBatch() 方法執行 SQL 語句,并傳回 int 值的一個數組。該數組包含

受每條語句影響的行數。将 SELECT 語句或者其他傳回 ResultSet 的 SQL 語句放在一

個批進行中會導緻 SQLException。

清單 5 中列出了利用 java.sql.Statement 進行批處理的一個簡單執行個體。

清單 5. 實際運作中的批處理

Statement stmt = conn.createStatement();

stmt.insert("DELETE FROM Users");

stmt.insert("INSERT INTO Users VALUES('rod', 37, 'circle')");

stmt.insert("INSERT INTO Users VALUES('jane', 33, 'triangle')");

stmt.insert("INSERT INTO Users VALUES('freddy', 29, 'square')");

int[] counts = stmt.executeBatch();

在您不知道特定語句将運作的次數時,批處理是一個處理 SQL 代碼的好方法。例如,如

果在不使用批處理的情況下試圖插入 100 條記錄,那麼性能可能會受到影響。如果編寫

一個腳本,增加 10000 條記錄,那麼情況會變得更糟。添加批處理可以幫助提高性能,

後者甚至能夠提高代碼的可讀性。

Java 對象序列化并不支援批處理。通常,會在某個對象的範圍(聯系圖)上運用序列化

,在這種情況下,批處理沒有意義。是以,批處理在資料更新的定時和分組方面為您提

供一定的靈活性,而這些對于序列化來說不一定是可用的。

從 Java 代碼調用存儲過程

存儲過程是一組 SQL 語句,它們建立了一個邏輯單元,并執行特定任務。可以用存儲過

程來封裝一個操作或者查詢的集合,這些操作或查詢都将在一個資料庫伺服器上執行。

存儲過程是在資料庫伺服器中被編譯和存儲的。是以,每次調用存儲過程時,DBMS 都将

重用已編譯的二進制代碼,是以執行速度會更快。

JDBC 允許您從 Java 應用程式中調用資料庫存儲過程。第一步是建立 CallableStatem

ent 對象。與 Statement 和 PreparedStatement 對象一樣,這項操作是用一個打開的

Connection 對象完成的。CallableStatement 對象包含對存儲過程的調用;但它并不

包含存儲過程自身。清單 6 中的第一行代碼使用 con 連接配接建立了對存儲過程 SHOW_AC

COUNT 的調用。波形括号中包覆的部分是存儲過程的轉義文法。當驅動程式遇到 {call

SHOW_ACCOUNT} 時,它将該轉義文法翻譯成資料庫所使用的本地 SQL,進而調用名為

SHOW_ACCOUNT 的存儲過程。

清單 6. 實際運作中的存儲過程

CallableStatement cs = con.prepareCall("{call SHOW_ACCOUNT(?)}");

cs.setInt(1,myaccountnumber);

ResultSet rs = cs.executeQuery();

假設 Sybase 中的存儲過程 SHOW_ACCOUNT 包含清單 7 中所示的代碼。

Listing 7. SHOW_ACCOUNT stored procedure

CREATE PROCEDURE SHOW_ACCOUNT (@Acc int)

AS

BEGIN

Select balance from USER_ACCOUNTS where Account_no = @Acc

END

ResultSet rs 看起來類似于:

balance

----------------

12000.95

注意,用來執行 cs 的方法是 executeQuery(),由于 cs 調用的存儲過程隻包含一個查

詢,是以隻産生一個結果集。如果該過程隻包含一個更新或者一個 DDL 語句,則将使用

executeUpdate() 方法。然而,有時候存在存儲過程包含多個 SQL 語句的情況,在這

種情況下,它将産生多個結果集、多個更新計數,或者結果集和更新計數的某種結合。

是以,應該使用 execute() 方法執行 CallableStatement。

CallableStatement 類是 PreparedStatement 的子類,是以 CallableStatement 對象

可以接受與 PreparedStatement 對象相同的參數。而且,CallableStatement 對象可以

接受輸出參數,并将該參數用于輸入和輸出。INOUT 參數和 execute() 方法通常很少使

用。要想處理 OUT 參數,需要通過使用 registerOutParameter(int, int) 方法将 OU

T 參數注冊到存儲過程。

舉例說明,我們假設 GET_ACCOUNT 過程包含清單 8 中的代碼。

清單 8. GET_ACCOUNT

CREATE PROCEDURE GET_ACCOUNT (@Acc int, @balance float OUTPUT)

AS

BEGIN

Select @balance = balance from USER_ACCOUNTS where Account_no = @Acc

END

在這個執行個體中,參數 balance 被聲明是一個 OUT 參數。現在,調用該過程的 JDBC 代

碼如清單 9 所示。

清單 9. 調用一個存儲過程的 JDBC 代碼

CallableStatement csmt = con.prepareCall("{GET_ACCOUNT(?,?)");

csmt.setInt(1,youraccountnumber);

csmt.registerOutParamter(2,java.sql.Types.FLOAT);

csmt.execute();

正使用 Java 序列化時,并不需要通路任何外部的系統,如 DBMS。換句話說,序列化是

一個純 Java 語言現象,它不涉及執行一個外部環境中的已編譯代碼。是以,在序列化

中不存在與 CallableStatement 對象相對應的機制。這意味着您不能将資料處理轉移到

外部系統或者元件中,盡管這些系統或者元件可能更适合它。

包裝

在讀完本文之後,我們希望您贊同:對于資料管理和持久化而言, JDBC 是比 Java 對

象序列化要好得多的方法。

JDBC 是一個用來通路資料存儲的極好的 API。 JDBC 最好的東西是它提供單一的 API

集合來通路多種資料源。使用者隻需要學習一個 API 集合,就可以通路任何資料源,這些

資料源可以是關系型的、層次型的或者任何其他格式。您需要的隻是一個 JDBC 驅動程

序,用它連接配接到目标資料源。JDBC 做了大量工作,将所有技術細節都封裝到了一個實作

軟體包中,進而将程式員從供應商特定的桎梏中解放出來。

表 1 對比了 JDBC 和 Java 對象序列化的各種特性。

表 1. JDBC 對 Java 序列化

對象序列化 JDBC

資料管理 使用檔案系統存儲序列化對象格式。這些系統中不包括特定的資料管理系統

。序列化對象(存儲在普通檔案中的)通常是以自己的特殊方式通過底層 OS 來管理的

。 使用一個 EAI/資料庫來存儲資料。EAI 或者資料庫具有一個用來管理資料源中的數

據指定的資料庫管理系統(DBMS)。JDBC 是将請求發送到 DBMS 的 JVM 和 DBMS 之間

的接口。JDBC 自身并不具有任何資料管理功能。

資料結構 底層的資料結構是一個對象圖。序列化将 Java 對象的狀态寫入到檔案系統

。 底層的資料結構可以是關系型的、層次型的,或者是網絡形狀。但是資料的邏輯視

圖通常是一個表。

資料定義 資料定義涉及到使用序列化語義建立一個可序列化對象并持久化該對象。

資料定義涉及到在目标資料存儲中建立必要的表,并且提供實體集之間的域級關系的明

确定義。這一般是通過使用目标 DBMS 所提供的軟體來完成的。

資料檢索 資料檢索涉及反序列化對象,并使用通路者方法讀取對象的屬性。 DBMS 提

供一個特殊的資料子語言來檢索資料。通過 JDBC API 可以将以這種資料子語言編寫的

語句傳遞給目标資料源。DBMS 負責驗證、執行和傳回該語句的結果。

安全 沒有可以使用的定義良好的安全機制。然而,底層的 OS 可以提供已序列化檔案

的安全。 DBMS 提供一個廣泛的安全特性集合。它可以完成認證和授權的工作。在可以

通路或者操作 DBMS 上的資料之前,JDBC API 需要給目标 DBMS發送證書。

事務 沒有可以使用的特定的事務控制機制。通過使用其他的 J2EE API,例如 JTA 或

者 JTS,可以在程式上維護事務。 DBMS 提供複雜的事務管理。JDBC API 提供有用的

方法來送出和復原事務。

并發控制 沒有可以使用的特定的并發控制機制。不過,通過使用 Java 語言中的同步

技術可以獲得并發控制的效果。 DBMS 提供多種等級的事務隔離。可以使用 JDBC API

方法來選擇一個特定等級的隔離。

Java 對象序列化和 JDBC 是 Java 技術領域中許多資料持久化機制中的兩種。在需要在

多個 JVM 之間以 Java 語言特定格式共享資料(例如用 RMI 的按值傳遞機制共享資料

)時,序列化最适合不過。然而,Java 序列化并不适用于企業資料,需要以一種定義良

好的結構對這些資料進行組織。在這樣的企業系統中,需要在多個系統和子系統之間共

享資料,而這些系統并不一定都與 Java 語言相容。在這種情況中,對象序列化根本不

能工作。

JDBC 提供一個公用 API來通路異構的資料存儲。它是 JVM 和目标 DBMS 之間的粘合劑

。它提供了一個使用 Java 平台通路資料存儲和維護企業資料的綱領性方法。然而,執

行 CRUD 操作所需的所有代碼都是由開發人員編寫。

為了在企業環境中最有效地使用 JDBC,架構設計人員需要分析其企業中的資料,并開發

一個用于資料持久性的架構。由于使用 JDBC 持久化資料的機制與系統想要解決的商業

問題無關,是以強烈建議将資料持久性層與應用程式的商業邏輯相分離。設計模式對設

計這種架構非常有幫助。