?? cachedrowset.htm
字號:
int id = crs.getInt("ID");
Clob comment = crs.getClob("COM");
short dept = crs.getShort("DEPT");
System.out.println(name + " " + id + " " + comment + " " + dept);
}
</PRE>
<H4>2.1 檢索 <CODE>RowSetMetaData</CODE></H4>通過在 <CODE>RowSetMetaData</CODE> 對象上調用
<CODE>ResultSetMetaData</CODE> 和 <CODE>RowSetMetaData</CODE> 的方法,應用程序可以獲得有關
<CODE>CachedRowSet</CODE> 對象中各列的信息。以下代碼片斷(其中 <I>crs</I> 是一個
<CODE>CachedRowSet</CODE> 對象)展示了該過程。第一行使用關于 <I>crs</I> 中各列的信息創建一個
<CODE>RowSetMetaData</CODE> 對象。繼承自 <CODE>ResultSet</CODE> 接口的方法
<CODE>getMetaData</CODE> 返回一個 <CODE>ResultSetMetaData</CODE> 對象,將該對象分配給變量
<I>rsmd</I> 前會將其強制轉換為 <CODE>RowSetMetaData</CODE> 對象。第二行查明 <I>jrs</I>
的列數,第三行獲得存儲在 <CODE>jrs</CODE> 第二列中 JDBC 類型的值。 <PRE> RowSetMetaData rsmd = (RowSetMetaData)crs.getMetaData();
int count = rsmd.getColumnCount();
int type = rsmd.getColumnType(2);
</PRE><CODE>RowSetMetaData</CODE> 接口與 <CODE>ResultSetMetaData</CODE> 接口有兩方面不同。
<UL>
<LI><I>它包括<CODE>設置</CODE>方法:</I>當使用取自不同 <CODE>ResultSet</CODE> 對象的數據填充
<CODE>RowSet</CODE> 對象時,該 RowSet 對象在內部使用這些方法。
<P></P>
<LI><I>它包含較少的<CODE>獲取</CODE>方法:</I>某些 <CODE>ResultSetMetaData</CODE> 方法無法應用到
<CODE>RowSet</CODE> 對象。例如,不會應用那些檢索某個列值是可寫入的還是只讀的方法,因為 <CODE>RowSet</CODE>
對象的所有列要么是可寫入的,要么是只讀的,這取決于該 rowset 是否可更新。 </LI></UL>注:要返回
<CODE>RowSetMetaData</CODE> 對象,實現必須重寫 <CODE>java.sql.ResultSet</CODE> 中定義的
<CODE>getMetaData()</CODE> 方法返回 <CODE>RowSetMetaData</CODE> 對象。
<H3>3.0 更新 <CODE>CachedRowSet</CODE> 對象</H3>更新 <CODE>CachedRowSet</CODE> 對象與更新
<CODE>ResultSet</CODE> 對象類似,但是因為更新 rowset
時它并未連接到其數據源,所以必須執行額外的步驟才能使更改在底層數據源中生效。調用方法 <CODE>updateRow</CODE> 或
<CODE>insertRow</CODE> 后,<CODE>CachedRowSet</CODE> 對象還必須調用方法
<CODE>acceptChanges</CODE> 使更新寫入數據源。以下示例(其中指針在 <CODE>CachedRowSet</CODE> 對象
<I>crs</I> 中的行上)顯示了更新當前行中兩個列值并同樣更新 <CODE>RowSet</CODE> 對象的底層數據源所需的代碼。 <PRE> crs.updateShort(3, 58);
crs.updateInt(4, 150000);
crs.updateRow();
crs.acceptChanges();
</PRE>
<P>下一個示例演示了移至插入行、在插入行上構建新行、將新行插入 rowset,然后調用方法 <CODE>acceptChanges</CODE>
將新行添加到底層數據源。注意,與獲取方法一樣,更新方法可以采用列索引或列名來指定所操作的列。 <PRE> crs.moveToInsertRow();
crs.updateString("Name", "Shakespeare");
crs.updateInt("ID", 10098347);
crs.updateShort("Age", 58);
crs.updateInt("Sal", 150000);
crs.insertRow();
crs.moveToCurrentRow();
crs.acceptChanges();
</PRE>
<P>注:<CODE>insertRow()</CODE> 方法在何處插入 <CODE>CachedRowSet</CODE>
對象的插入行內容是由實現定義的。<CODE>CachedRowSet</CODE> 接口的參考實現緊隨當前行插入新行,但也可以實現為在任何其他位置插入新行。
<P>有關這些示例的另一個注意點是它們使用方法 <CODE>acceptChanges</CODE> 的方式。通過內部調用
<CODE>RowSet</CODE> 對象的 writer 將這些更改寫入數據源,從而將 <CODE>CachedRowSet</CODE>
對象中的更改傳播回底層數據源的正是此方法。為此,writer 不得不承受建立到數據源的連接所帶來的開銷。上述兩個代碼片斷在調用
<CODE>updateRow</CODE> 或 <CODE>insertRow</CODE> 后立即調用方法
<CODE>acceptChanges</CODE>。但是,如果更改了多個行,則更高效的做法是在調用所有 <CODE>updateRow</CODE> 和
<CODE>insertRow</CODE> 后再調用 <CODE>acceptChanges</CODE>。如果只調用
<CODE>acceptChanges</CODE> 一次,則只需要建立一個連接。
<P>
<H3>4.0 更新底層數據源</H3>執行 <CODE>acceptChanges</CODE> 方法時,在后臺調用
<CODE>CachedRowSet</CODE> 對象的 writer(一個 <CODE>RowSetWriterImpl</CODE> 對象),以便將對
rowset 所作的更改寫入底層數據源。實現該 writer 以建立到數據源的連接并寫入更新。
<P>可通過 <CODE>SyncProvider</CODE> 接口的實現提供 writer,這已第 1 部分“創建
<CODE>CachedRowSet</CODE> 對象”中討論。默認的參考實現提供程序 <CODE>RIOptimisticProvider</CODE>
會實現其 writer 使用樂觀并發控制 (optimistic concurrency control) 機制。也就是說,在 rowset
與數據庫斷開時它不對底層數據庫維持任何鎖定,在將數據寫入數據源之前它只是檢查是否有沖突。如果存在沖突,則不向數據源寫入任何內容。
<P><CODE>SyncProvider</CODE> 類提供的 reader/writer
設施是可插入的,允許自定義數據的檢索和更新。如果需要其他的并發控制機制,可使用方法 <CODE>setSyncProvider</CODE> 插入其他
<CODE>SyncProvider</CODE> 實現。
<P>要使用樂觀并發控制例程,<CODE>RIOptismisticProvider</CODE>
要同時維護其當前值及其原始值(剛好位于當前值之前的值)。注意,如果沒有對 <CODE>RowSet</CODE>
對象中的數據進行任何更改,則其當前值和原始值相同,都是最初填充 <CODE>RowSet</CODE> 對象時使用的值。但是,一旦更改了
<CODE>RowSet</CODE> 對象中的任何值,當前值和原始值就不同了,盡管此時原始值仍是最初的值。隨著后續對 <CODE>RowSet</CODE>
對象中的數據進行更改,其原始值和當前值仍保持不同,但是其原始值將是前一個當前值。
<P>關注原始值允許 writer 對 <CODE>RowSet</CODE> 對象的原始值和數據庫中的值進行比較。如果數據庫中的值與
<CODE>RowSet</CODE> 對象的原始值不同,則意味著數據庫中的值已經更改,出現了沖突。writer
是否檢查沖突、檢查的程度如何,以及它如何處理沖突都取決于它的實現方式。
<P>
<H3>5.0 注冊和通知偵聽器</H3>作為 JavaBeans 組件,參與 JavaBeans 事件模型的所有 rowset
都繼承了用來注冊偵聽器和用來通知這些偵聽器 <CODE>BaseRowSet</CODE>
類中發生更改的各種方法。<CODE>CachedRowSet</CODE> 對象的偵聽器是一個組件,只要 rowset 中發生更改,它就應得到通知。例如,如果
<CODE>CachedRowSet</CODE> 對象包含查詢的結果,并且這些結果將以表格和條形圖之類的形式顯示,則可以向 rowset
將該表格和條形圖注冊為偵聽器,這樣它們可以更新以反映各種更改。要成為偵聽器,表格和條形圖類必須實現 <CODE>RowSetListener</CODE>
接口。然后可將它們添加到 <CODE>CachedRowSet</CODE> 對象的偵聽器列表,如以下代碼行所示。 <PRE>
crs.addRowSetListener(table);
crs.addRowSetListener(barGraph);
</PRE>每個移動指針或更改數據的 <CODE>CachedRowSet</CODE> 方法也將更改通知已注冊的偵聽器,所以當
<CODE>crs</CODE> 中發生更改時 <CODE>table</CODE> 和 <CODE>barGraph</CODE> 將得到通知。
<P>
<H3>6.0 向瘦客戶端傳遞數據</H3>使用 <CODE>CachedRowSet</CODE>
對象的主要原因之一是要在應用程序的不同組件之間傳遞數據。因為 <CODE>CachedRowSet</CODE>
對象是可序列化的,所以可使用它(舉例來說)將運行于服務器環境的企業 JavaBeans 組件執行查詢的結果通過網絡發送到運行于 web 瀏覽器的客戶端。
<P>由于 <CODE>CachedRowSet</CODE> 對象是非連接的,所以和具有相同數據的 <CODE>ResultSet</CODE>
對象相比更為簡潔。因此,它特別適于向瘦客戶端(如 PDA)發送數據,這種瘦客戶端由于資源限制或安全考慮而不適于使用 JDBC 驅動程序。所以
<CODE>CachedRowSet</CODE> 對象可提供一種“獲取各行”的方式而無需實現全部 JDBC API。
<P>
<H3>7.0 滾動和更新</H3><CODE>CachedRowSet</CODE> 對象的第二個主要用途是為那些本身不提供滾動和更新的
<CODE>ResultSet</CODE> 對象提供這些功能。換句話說,當 DBMS 不提供對滾動和更新的完全支持時,可使用
<CODE>CachedRowSet</CODE> 對象擴充啟用 JDBC 技術的驅動程序(以下稱為“JDBC 驅動程序”)的功能。要使不可滾動和只讀的
<CODE>ResultSet</CODE> 對象變得可滾動和可更新,程序員只需創建一個使用該 <CODE>ResultSet</CODE> 對象的數據所填充的
<CODE>CachedRowSet</CODE> 對象即可。以下代碼片斷演示了這一過程,其中 <CODE>stmt</CODE> 是一個
<CODE>Statement</CODE> 對象。 <PRE> ResultSet rs = stmt.executeQuery("SELECT * FROM EMPLOYEES");
CachedRowSetImpl crs = new CachedRowSetImpl();
crs.populate(rs);
</PRE>
<P>現在對象 <CODE>crs</CODE> 與對象 <CODE>rs</CODE> 一樣,也包含了取自表 <CODE>EMPLOYEES</CODE>
的數據。不同的是 <CODE>crs</CODE> 的指針可以向前、向后移動,或者移動到特定行,即使 <CODE>rs</CODE>
的指針只能向前移動也是如此。此外,即使 <CODE>rs</CODE> 是不可更新的,<CODE>crs</CODE>
也將是可更新的,因為在默認情況下,<CODE>CachedRowSet</CODE> 對象是可滾動和可更新的。
<P>總之,可將 <CODE>CachedRowSet</CODE>
對象簡單地看成是一個非連接的行集合,這些行將緩存在數據源外部。由于它比較小并且是可序列化的,所以它可以輕松地通過導線發送,并且非常適合于向瘦客戶端發送數據。但是
<CODE>CachedRowSet</CODE> 對象也有局限性:它的大小限制在它一次可在內存中存儲的數據量范圍內。
<P>
<H3>8.0 獲得通用數據訪問</H3><CODE>CachedRowSet</CODE>
類的另一個優勢在于它能夠從關系數據庫以外各種數據源檢索并存儲數據。可以實現 rowset 的 reader
讀取任何表格數據源(包括電子表格或平面文件)的數據,并用該數據填充其 rowset。因為 <CODE>CachedRowSet</CODE>
對象及其元數據都可以從頭創建,所以充當 rowset 工廠的組件可以使用此功能來創建一個包含非 SQL 數據源數據的 rowset。但是,大部分情況下,希望
<CODE>CachedRowSet</CODE> 對象包含使用 JDBC API 從 SQL 數據庫中獲取的數據。
<P>
<H3>9.0 設置屬性</H3>所有 rowset 都維護一個屬性集,通常使用某種工具來設置這些屬性。rowset 具有的屬性的數量和種類各不相同,這取決于
rowset 的用途及其獲得數據的方式。例如,從 <CODE>ResultSet</CODE> 對象獲得其數據的 rowset
需要設置那些建立數據庫連接所需的屬性。如果某個 rowset 使用 <CODE>DriverManager</CODE>
設施建立連接,則它需要設置一個標識合適驅動程序的 JDBC URL 屬性,還需要設置那些提供用戶名和密碼的屬性。另一方面,如果 rowset 使用
<CODE>DataSource</CODE> 對象建立連接(這是首選的方法),則它無需設置 JDBC URL
屬性。但是它需要設置用于數據源邏輯名、用戶名和密碼的屬性。
<P>注:要使用 <CODE>DataSource</CODE> 對象建立連接,該 <CODE>DataSource</CODE> 對象必須已經向使用 Java
Naming and Directory Interface<SUP><FONT size=-2>TM</FONT></SUP> (JNDI) API
的命名服務注冊。通常由具有系統管理員資格的人員完成此注冊。
<P>為了能夠使用數據庫的數據填充,rowset 需要設置 command 屬性。此屬性是一種 <CODE>PreparedStatement</CODE>
對象的查詢,該對象允許查詢具有在運行時(而不是設計時)設置的參數占位符。要用各種值設置這些占位符參數,rowset 要為設置每種數據類型的值提供設置方法,類似于
<CODE>PreparedStatement</CODE> 接口提供的設置方法。
<P>以下代碼片斷展示了如何設置 <CODE>CachedRowSet</CODE> 對象 <CODE>crs</CODE> 的 command
屬性。注意,如果使用某種工具設置屬性,則這就是該工具應使用的代碼。 <PRE> crs.setCommand("SELECT FIRST_NAME, LAST_NAME, ADDRESS FROM CUSTOMERS " +
"WHERE CREDIT_LIMIT > ? AND REGION = ?");
</PRE>
<P>用于設置該命令占位符參數的值被包含在 <CODE>RowSet</CODE> 對象的 <CODE>params</CODE> 字段中,該字段是一個
<CODE>Vector</CODE> 對象。<CODE>CachedRowSet</CODE> 類為設置其 <CODE>params</CODE>
字段中的元素提供了一組設置方法。以下代碼片斷演示了如何設置前一個示例查詢中的兩個參數。 <PRE> crs.setInt(1, 5000);
crs.setString(2, "West");
</PRE>
<P><CODE>params</CODE>
字段現在包含兩個元素,每個元素都是一個兩元素長的數組。第一個元素是參數號;第二個元素是要設置的值。在這種情況下,<CODE>params</CODE>
的第一個元素是 <CODE>1</CODE>,<CODE>5000</CODE>,第二個元素是
<CODE>2</CODE>,<CODE>"West"</CODE>。當應用程序調用方法 <CODE>execute</CODE> 時,它會依次調用此
<CODE>RowSet</CODE> 對象的 reader,該 reader 會依次調用其 <CODE>readData</CODE>
方法。作為實現的一部分,<CODE>readData</CODE> 將獲得 <CODE>params</CODE>
中的值并使用這些值設置命令的占位符參數。以下代碼片斷說明了在獲得 <CODE>Connection</CODE> 對象 <CODE>con</CODE> 后
reader 如何執行此操作。 <PRE> PreparedStatement pstmt = con.prepareStatement(crs.getCommand());
reader.decodeParams();
// decodeParams figures out which setter methods to use and does something
// like the following:
// for (i = 0; i < params.length; i++) {
// pstmt.setObject(i + 1, params[i]);
// }
</PRE>
<P>這里用于 <CODE>crs</CODE> 的命令是查詢 <CODE>"SELECT FIRST_NAME, LAST_NAME, ADDRESS
FROM CUSTOMERS WHERE CREDIT_LIMIT > 5000 AND REGION =
"West"</CODE>。<CODE>readData</CODE> 方法使用以下代碼行執行此命令后,它會獲得 <CODE>rs</CODE>
的數據,該數據用于填充 <CODE>crs</CODE>。 <PRE> ResultSet rs = pstmt.executeQuery();
</PRE>
<P>上述代碼片斷說明了在后臺進行的操作;這些操作不會出現在應用程序中,因為應用程序不會調用 <CODE>readData</CODE> 和
<CODE>decodeParams</CODE> 之類的方法。相反,以下代碼片斷展示了應用程序可能執行的操作。它設置 rowset 的命令、設置
command 屬性并執行該命令。只需調用 <CODE>execute</CODE> 方法,就可使用從表 <CODE>CUSTOMERS</CODE>
請求的數據生成 <CODE>crs</CODE>。 <PRE> crs.setCommand("SELECT FIRST_NAME, LAST_NAME, ADDRESS FROM CUSTOMERS" +
"WHERE CREDIT_LIMIT > ? AND REGION = ?");
crs.setInt(1, 5000);
crs.setString(2, "West");
crs.execute();
</PRE>
<H3>10.0 分頁數據</H3>因為 <CODE>CachedRowSet</CODE>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -