CachedRowSet使用


public interface CachedRowSet     
      extends RowSet,Joinable
     
View Code

  所有標准 CachedRowSet 實現都必須實現的接口。Sun Microsystems 提供的 CachedRowSet 接口的參考實現是一個標准實現。開發人員可以按原樣使用此實現、可以擴展它,也可以選擇自己編寫此接口的實現。CachedRowSet 對象是一個數據行的容器,可在內存中緩存其各行,這使得進行操作時無需總是連接到數據源。此外,它還是一個 JavaBeansTM 組件,是可滾動、可更新、可序列化的。CachedRowSet 對象通常包含結果集中的行,但它也可以包含任何具有表格式的文件(如電子表格)中的行。參考實現只支持從 ResultSet 對象中獲取數據,但是開發人員可以擴展 SyncProvider 實現,以提供對其他表格數據源的訪問。
  應用程序可以修改 CachedRowSet 對象中的數據,這些修改隨后可以被傳播回數據源。CachedRowSet 對象是一個非連接 rowset,這意味着它只會短暫地連接其數據源。連接數據源發生在讀取數據以用各行填充自身,以及將更改傳播回其底層數據源時。其余時間 CachedRowSet 對象是非連接的,包括修改它的數據時。非連接使 RowSet 對象更為簡潔,因此更容易傳遞給另一個組件。例如,非連接 RowSet 對象可以被序列化並通過導線傳遞到瘦客戶端 (thin client)。

1.0 創建 CachedRowSet 對象

  以下代碼行使用默認 CachedRowSet 構造方法來創建默認的 CachedRowSet 對象。

 CachedRowSetImpl crs = new CachedRowSetImpl();
 
View Code

  CachedRowSet 對象的屬性設置為 BaseRowSet 對象的默認屬性,此外,它將 RIOptimisticProvider 對象作為其同步提供者。 RIOptimisticProvider(RI 中包含的兩個 SyncProvider 實現之一)是在沒有指定同步提供者時, SyncFactory 單件 (singleton) 將提供的默認提供者。
  SyncProvider 對象提供了帶有 reader 的(RowSetReader 對象)的 CachedRowSet 對象,用於從數據源讀取數據以便用該數據填充自身。可以實現 reader 從 ResultSet 對象或者表格式的文件中讀取數據。SyncProvider 對象還提供了 writer(RowSetWriter 對象),用於同步在與底層數據源中的數據斷開連接時對 CachedRowSet 對象數據所做的任何更改。
  可以實現 writer 以在檢查和避免沖突方面實施不同程度的關注。(如果使用某個值填充 rowset 后更改了數據源中的該值,則會發生沖突。)RIOptimisticProvider 實現假定沖突很少或沒有沖突,因此不設置鎖定。僅在沒有沖突時,它才使用取自 CachedRowSet 對象的值更新數據源。也可以實現其他 writer,使其始終可將修改后的數據寫入數據源,這可以通過不檢查沖突來實現,或者從另一個方面着手,即通過設置足夠的鎖定來防止對數據源中的數據進行更改。這兩種 writer 之間還可以有其他 writer 實現。
  CachedRowSet 對象可以使用任何已向 SyncFactory 單件注冊的 SyncProvider 實現。通過調用以下代碼行,應用程序可以找到已注冊的 SyncProvider 實現。

java.util.Enumeration providers = SyncFactory.getRegisteredProviders();
View Code

  CachedRowSet 對象可使用兩種方式來指定它將使用的 SyncProvider 對象。 向構造方法提供實現名.以下代碼行創建 CachedRowSet 對象 crs2,使用默認值初始化該對象,但其 SyncProvider 對象是指定的。

 CachedRowSetImpl crs2 = new CachedRowSetImpl( "com.fred.providers.HighAvailabilityProvider");
View Code

  使用 CachedRowSet 方法 setSyncProvider 設置 SyncProvider.以下代碼行為 crs 重置 SyncProvider 對象,該 CachedRowSet 對象是使用默認構造方法創建的。  

crs.setSyncProvider("com.fred.providers.HighAvailabilityProvider");
View Code

2.0 從 CachedRowSet 對象獲取數據

  從 CachedRowSet 對象獲取數據可使用繼承自 ResultSet 接口的獲取方法。

    while (crs.next()) {
        String name = crs.getString(1);
        int id = crs.getInt(2);
        Clob comment = crs.getClob(3);
        short dept = crs.getShort(4);
        System.out.println(name + "  " + id + "  " + comment + "  " + dept);
    }
 
    while (crs.next()) {
        String name = crs.getString("NAME");
        int id = crs.getInt("ID");
        Clob comment = crs.getClob("COM");
        short dept = crs.getShort("DEPT");
        System.out.println(name + "  " + id + "  " + comment + "  " + dept);
    }
 
View Code

2.1 獲取 RowSetMetaData

  通過在 RowSetMetaData 對象上調用 ResultSetMetaData 和 RowSetMetaData 的方法,應用程序可以獲得有關 CachedRowSet 對象中各列的信息。

RowSetMetaData rsmd = (RowSetMetaData)crs.getMetaData();
int count = rsmd.getColumnCount();
int type = rsmd.getColumnType(2);
View Code

  注:要返回 RowSetMetaData 對象,實現必須重寫 java.sql.ResultSet 中定義的 getMetaData() 方法返回 RowSetMetaData 對象。

3.0 更新 CachedRowSet

  對象更新 CachedRowSet 對象與更新 ResultSet 對象類似,但是因為更新 rowset 時它並未連接到其數據源,所以必須執行額外的步驟才能使更改在底層數據源中生效。調用方法 updateRow 或 insertRow 后,CachedRowSet 對象還必須調用方法 acceptChanges 使更新寫入數據源。

crs.updateShort(3, 58);
crs.updateInt(4, 150000);
crs.updateRow();
crs.acceptChanges();
View Code

  下一個示例演示了移至插入行、在插入行上構建新行、將新行插入 rowset,然后調用方法 acceptChanges 將新行添加到底層數據源。注意,與獲取方法一樣,更新方法可以采用列索引或列名來指定所操作的列。

     crs.moveToInsertRow();
     crs.updateString("Name", "Shakespeare");
     crs.updateInt("ID", 10098347);
     crs.updateShort("Age", 58);
     crs.updateInt("Sal", 150000);
     crs.insertRow();
     crs.moveToCurrentRow();
     crs.acceptChanges();
View Code

  注:insertRow() 方法在何處插入 CachedRowSet 對象的插入行內容是由實現定義的。CachedRowSet 接口的參考實現緊隨當前行插入新行,但也可以實現為在任何其他位置插入新行。
  有關這些示例的另一個注意點是它們使用方法 acceptChanges 的方式。通過內部調用 RowSet 對象的 writer 將這些更改寫入數據源,從而將 CachedRowSet 對象中的更改傳播回底層數據源的正是此方法。為此,writer 不得不承受建立到數據源的連接所帶來的開銷。上述兩個代碼片斷在調用 updateRow 或 insertRow 后立即調用方法 acceptChanges。但是,如果更改了多個行,則更高效的做法是在調用所有 updateRow 和 insertRow 后再調用 acceptChanges。如果只調用 acceptChanges 一次,則只需要建立一個連接。

4.0 更新底層數據源

  執行 acceptChanges 方法時,在后台調用 CachedRowSet 對象的 writer(一個 RowSetWriterImpl 對象),以便將對 rowset 所作的更改寫入底層數據源。實現該 writer 以建立到數據源的連接並寫入更新。
  可通過 SyncProvider 接口的實現提供 writer,這已第 1 部分“創建 CachedRowSet 對象”中討論。默認的參考實現提供者 RIOptimisticProvider 會實現其 writer 使用樂觀並發控制 (optimistic concurrency control) 機制。也就是說,在 rowset 與數據庫斷開時它不對底層數據庫維持任何鎖定,在將數據寫入數據源之前它只是檢查是否有沖突。如果存在沖突,則不向數據源寫入任何內容。
  SyncProvider 類提供的 reader/writer 設施是可插入的,允許自定義數據的獲取和更新。如果需要其他的並發控制機制,可使用方法 setSyncProvider 插入其他 SyncProvider 實現。
  要使用樂觀並發控制例程,RIOptismisticProvider 要同時維護其當前值及其原始值(剛好位於當前值之前的值)。注意,如果沒有對 RowSet 對象中的數據進行任何更改,則其當前值和原始值相同,都是最初填充 RowSet 對象時使用的值。但是,一旦更改了 RowSet 對象中的任何值,當前值和原始值就不同了,盡管此時原始值仍是最初的值。隨着后續對 RowSet 對象中的數據進行更改,其原始值和當前值仍保持不同,但是其原始值將是前一個當前值。
  關注原始值允許 writer 對 RowSet 對象的原始值和數據庫中的值進行比較。如果數據庫中的值與 RowSet 對象的原始值不同,則意味着數據庫中的值已經更改,出現了沖突。writer 是否檢查沖突、檢查的程度如何,以及它如何處理沖突都取決於它的實現方式。

5.0 注冊和通知偵聽器

  作為 JavaBeans 組件,參與 JavaBeans 事件模型的所有 rowset 都繼承了用來注冊偵聽器和用來通知這些偵聽器 BaseRowSet 類中發生更改的各種方法。CachedRowSet 對象的偵聽器是一個組件,只要 rowset 中發生更改,它就應得到通知。例如,如果 CachedRowSet 對象包含查詢的結果,並且這些結果將以表格和條形圖之類的形式顯示,則可以向 rowset 將該表格和條形圖注冊為偵聽器,這樣它們可以更新以反映各種更改。要成為偵聽器,表格和條形圖類必須實現 RowSetListener 接口。然后可將它們添加到 CachedRowSet 對象的偵聽器列表,如以下代碼行所示。

    crs.addRowSetListener(table);
    crs.addRowSetListener(barGraph);
View Code

  每個移動指針或更改數據的 CachedRowSet 方法也將更改通知已注冊的偵聽器,所以當 crs 中發生更改時 table 和 barGraph 將得到通知。

6.0 向瘦客戶端傳遞數據

  使用 CachedRowSet 對象的主要原因之一是要在應用程序的不同組件之間傳遞數據。因為 CachedRowSet 對象是可序列化的,所以可使用它(舉例來說)將運行於服務器環境的企業 JavaBeans 組件執行查詢的結果通過網絡發送到運行於 web 瀏覽器的客戶端。
  由於 CachedRowSet 對象是非連接的,所以和具有相同數據的 ResultSet 對象相比更為簡潔。因此,它特別適於向瘦客戶端(如 PDA)發送數據,這種瘦客戶端由於資源限制或安全考慮而不適於使用 JDBC 驅動程序。所以 CachedRowSet 對象可提供一種“獲取各行”的方式而無需實現全部 JDBC API。

7.0 滾動和更新

  CachedRowSet 對象的第二個主要用途是為那些本身不提供滾動和更新的 ResultSet 對象提供這些功能。換句話說,當 DBMS 不提供對滾動和更新的完全支持時,可使用 CachedRowSet 對象擴充啟用 JDBC 技術的驅動程序(以下稱為“JDBC 驅動程序”)的功能。要使不可滾動和只讀的 ResultSet 對象變得可滾動和可更新,程序員只需創建一個使用該 ResultSet 對象的數據所填充的 CachedRowSet 對象即可。以下代碼片斷演示了這一過程,其中 stmt 是一個 Statement 對象。

    ResultSet rs = stmt.executeQuery("SELECT * FROM EMPLOYEES");
    CachedRowSetImpl crs = new CachedRowSetImpl();
    crs.populate(rs);
View Code

  現在對象 crs 與對象 rs 一樣,也包含了取自表 EMPLOYEES 的數據。不同的是 crs 的指針可以向前、向后移動,或者移動到特定行,即使 rs 的指針只能向前移動也是如此。此外,即使 rs 是不可更新的,crs 也將是可更新的,因為在默認情況下,CachedRowSet 對象是可滾動和可更新的。總之,可將 CachedRowSet 對象簡單地看成是一個非連接的行集合,這些行將緩存在數據源外部。由於它比較小並且是可序列化的,所以它可以輕松地通過導線發送,並且非常適合於向瘦客戶端發送數據。但是 CachedRowSet 對象也有局限性:它的大小限制在它一次可在內存中存儲的數據量范圍內。

8.0 獲得通用數據訪問

  CachedRowSet 類的另一個優勢在於它能夠從關系數據庫以外各種數據源獲取並存儲數據。可以實現 rowset 的 reader 讀取任何表格數據源(包括電子表格或平面文件)的數據,並用該數據填充其 rowset。因為 CachedRowSet 對象及其元數據都可以從頭創建,所以充當 rowset 工廠的組件可以使用此功能來創建一個包含非 SQL 數據源數據的 rowset。但是,大部分情況下,希望 CachedRowSet 對象包含使用 JDBC API 從 SQL 數據庫中獲取的數據。

9.0 設置屬性

  所有 rowset 都維護一個屬性集,通常使用某種工具來設置這些屬性。rowset 具有的屬性的數量和種類各不相同,這取決於 rowset 的用途及其獲得數據的方式。例如,從 ResultSet 對象獲得其數據的 rowset 需要設置那些建立數據庫連接所需的屬性。如果某個 rowset 使用 DriverManager 設施建立連接,則它需要設置一個標識合適驅動程序的 JDBC URL 屬性,還需要設置那些提供用戶名和密碼的屬性。另一方面,如果 rowset 使用 DataSource 對象建立連接(這是首選的方法),則它無需設置 JDBC URL 屬性。但是它需要設置用於數據源邏輯名、用戶名和密碼的屬性。
  注:要使用 DataSource 對象建立連接,該 DataSource 對象必須已經向使用 Java Naming and Directory InterfaceTM (JNDI) API 的命名服務注冊。通常由具有系統管理員資格的人員完成此注冊。
  為了能夠使用數據庫的數據填充,rowset 需要設置 command 屬性。此屬性是一種 PreparedStatement 對象的查詢,該對象允許查詢具有在運行時(而不是設計時)設置的參數占位符。要用各種值設置這些占位符參數,rowset 要為設置每種數據類型的值提供設置方法,類似於 PreparedStatement 接口提供的設置方法。
以下代碼片斷展示了如何設置 CachedRowSet 對象 crs 的 command 屬性。注意,如果使用某種工具設置屬性,則這就是該工具應使用的代碼。

crs.setCommand("SELECT FIRST_NAME, LAST_NAME, ADDRESS FROM       CUSTOMERS " +
                   "WHERE CREDIT_LIMIT > ? AND REGION = ?");
View Code

  用於設置該命令占位符參數的值被包含在 RowSet 對象的 params 字段中,該字段是一個 Vector 對象。CachedRowSet 類為設置其 params 字段中的元素提供了一組設置方法。以下代碼片斷演示了如何設置前一個示例查詢中的兩個參數。

    crs.setInt(1, 5000);
    crs.setString(2, "West");
View Code

  params 字段現在包含兩個元素,每個元素都是一個兩元素長的數組。第一個元素是參數號;第二個元素是要設置的值。在這種情況下,params 的第一個元素是 1,5000,第二個元素是 2,"West"。當應用程序調用方法 execute 時,它會依次調用此 RowSet 對象的 reader,該 reader 會依次調用其 readData 方法。作為實現的一部分,readData 將獲得 params 中的值並使用這些值設置命令的占位符參數。以下代碼片斷說明了在獲得 Connection 對象 con 后 reader 如何執行此操作。

    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]);
    //    }
View Code

  這里用於 crs 的命令是查詢 "SELECT FIRST_NAME, LAST_NAME, ADDRESS FROM CUSTOMERS WHERE CREDIT_LIMIT > 5000 AND REGION = "West"。readData 方法使用以下代碼行執行此命令后,它會獲得 rs 的數據,該數據用於填充 crs。

  ResultSet rs = pstmt.executeQuery();
View Code

  上述代碼片斷說明了在后台進行的操作;這些操作不會出現在應用程序中,因為應用程序不會調用 readData 和 decodeParams 之類的方法。相反,以下代碼片斷展示了應用程序可能執行的操作。它設置 rowset 的命令、設置 command 屬性並執行該命令。只需調用 execute 方法,就可使用從表 CUSTOMERS 請求的數據生成 crs。

    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();
 
View Code

10.0 分頁數據

  因為 CachedRowSet 對象在內存中存儲數據,所以它在任一時間可以包含的數據量是由可用的內存量決定的。要避開此限制,CachedRowSet 對象可以數據塊(稱為頁)的形式從 ResultSet 對象中獲取數據。要利用此機制,應用程序應使用方法 setPageSize 設置一頁中要包括的行數。換句話說,如果頁大小設置為 5,則一次從數據源中獲取一個 5 行的數據塊。應用程序也可選擇設置一次可獲取的最大行數。如果最大行數設置為 0,或者未設置最大行數,則對一次獲取的行數沒有限制。
  設置各個屬性后,必須使用方法 populate 或方法 execute 用數據填充 CachedRowSet 對象。以下代碼行演示了如何使用方法 populate。注意,該方法的這種形式采用兩個參數,ResultSet 句柄和 ResultSet 對象中的行,從該行開始獲取各行。

     CachedRowSet crs = new CachedRowSetImpl();
     crs.setMaxRows(20);
     crs.setPageSize(4);
     crs.populate(rsHandle, 10);
View Code

  運行此代碼時,將使用 rsHandle 中從第 10 行開始的 4 行數據填充 crs。
  下一個代碼片斷展示了如何使用方法 execute 填充 CachedRowSet 對象,該方法可以采用 Connection 對象作為一個參數,也可以不采用。此代碼向 execute 傳遞 Connection 對象 conHandle。注意,以下代碼片斷和上述代碼片斷有兩處差別。首先,沒有調用方法 setMaxRows,所以沒有對 crs 可以包含的行數設置限制。(記住,對於 crs 在內存中可以存儲的數據量,總是有一個最高限制。)第二個差別是不能向方法 execute 傳遞 ResultSet 對象中起始獲取行的行號。此方法始終從第一行開始獲取。

     CachedRowSet crs = new CachedRowSetImpl();
     crs.setPageSize(5);
     crs.execute(conHandle);
 
View Code

  運行此代碼后,crs 將包含由 crs 的命令所生成的 ResultSet 對象中的 5 行數據。crs 的 writer 將使用 conHandle 連接數據源並執行 crs 的命令。然后應用程序就能夠在 crs 中的數據上進行操作,方式與在任何其他 CachedRowSet 對象的數據上進行操作的方式相同。
  要訪問下一頁(數據塊),應用程序可調用方法 nextPage。此方法創建新的 CachedRowSet 對象並用下一頁的數據填充。例如,假定 CachedRowSet 對象的命令返回一個具有 1000 行數據的 ResultSet 對象 rs。如果頁大小設置為 100,則首次調用方法 nextPage 將創建一個包含 rs 前 100 行的 CachedRowSet 對象。在使用這前 100 行的數據執行完所需的操作后,應用程序可以再次調用方法 nextPage 創建另一個帶有 rs 第二個 100 行數據的 CachedRowSet 對象。第一個 CachedRowSet 對象中的數據不再存在於內存中,因為它已被第二個 CachedRowSet 對象的數據替換了。調用方法 nextPage 10 次后,第十個 CachedRowSet 對象將包含 rs 最后 100 行存儲在內存中的數據。在任意給定時間,內存中僅存儲一個 CachedRowSet 對象的數據。
  只要當前頁不是各行的最后一頁,方法 nextPage 就返回 true,沒有其他頁時,則返回 false。因此,可在 while 循環中使用它來獲取所有頁,正如在以下代碼行中所演示的。

 CachedRowSet crs = CachedRowSetImpl();
     crs.setPageSize(100);
     crs.execute(conHandle);

     while(crs.nextPage()) {
         while(crs.next()) {
             . . . // operate on chunks (of 100 rows each) in crs,
                   // row by row
         }
     }
 
View Code

  運行此代碼片斷后,應用程序會遍歷所有 1000 行,但是每次內存中的數據只有 100 行。
  CachedRowSet 接口還定義了方法 previousPage。正如方法 nextPage 類似於 ResultSet 方法 next,方法 previousPage 也類似於 ResultSet 方法 previous。與方法 nextPage 類似,previousPage 創建一個 CachedRowSet 對象,包含作為頁大小設置的行數。因此,(舉例來說)方法 previousPage 可用在上述代碼片斷末尾的 while 循環中,以從最后一頁開始逆向遍歷到第一頁。方法 previousPage 也與 nextPage 類似,因為它也可以用在 while 循環中,不同之處在於它是在前面還有頁時返回 true,前面沒有頁時返回 false。
  通過將指針定位於每頁最后一行的后面(如以下代碼片斷所執行的),方法 previous 就可以在每頁中從最后一行遍歷到第一行。代碼也可將指針置於每頁第一行的前面,然后在 while 循環中使用 next 方法,以在每頁中從最第一行遍歷到最后一行。
  以下代碼片斷假定是前一個代碼片斷的繼續,這意味着第十個 CachedRowSet 對象的指針位於最后一行。代碼將指針移到最后一行的后面,這樣第一次調用方法 previous 會將指針放回到最后一行上。遍歷最后一頁(CachedRowSet 對象 crs)的所有行后,代碼接着會進入 while 循環以獲得第九頁、向后遍歷各行、轉至第八頁、向后遍歷各行,依此類推,直到第一頁的第一行為止。

     crs.afterLast();
     while(crs.previous())  {
         . . . // navigate through the rows, last to first
     {
     while(crs.previousPage())  {
         crs.afterLast();
         while(crs.previous())  {
             . . . // go from the last row to the first row of each page
         }
     }
View Code


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM