A CachedRowSet object is special in that it can operate
      without being connected to its data source, that is, it is
      a disconnected RowSet object. It gets its name
      from the fact that it stores (caches) its
      data in memory so that it can operate on its own data
      rather than on the data stored in a database.
    
      The CachedRowSet interface is the superinterface for all
      disconnected RowSet objects, so everything demonstrated here also applies to WebRowSet, JoinRowSet, and
      FilteredRowSet objects.
    
      Note that although the data source for a CachedRowSet
      object (and the RowSet objects derived from it) is almost
      always a relational database, a CachedRowSet object is
      capable of getting data from any data source that stores
      its data in a tabular format. For example, a flat file or
      spreadsheet could be the source of data. This is true when
      the RowSetReader object for a disconnected RowSet object is
      implemented to read data from such a data source. The
      reference implementation of the CachedRowSet interface has
      a RowSetReader object that reads data from a relational
      database, so in this tutorial, the data source is always a
      database.
    
The following topics are covered:
Setting up a CachedRowSet object involves the following:
You can create a new CachedRowSet object in the different ways:
RowSetFactory, which is created from the class RowSetProvider: See Using the RowSetFactory Interface in Using JdbcRowSet Objects for more information.Note: Alternatively, you can use the constructor from the CachedRowSet implementation of your JDBC driver. However, implementations of the RowSet interface will differ from the reference implementation. These implementations will have different names and constructors. For example, the Oracle JDBC driver's implementation of the CachedRowSet interface is named oracle.jdbc.rowset.OracleCachedRowSet.
One of the ways you can create a CachedRowSet object is by
      calling the default constructor defined in the reference
      implementation, as is done in the following line of code:
    
CachedRowSet crs = new CachedRowSetImpl();
The object crs has the same default values for its properties
      that a JdbcRowSet object has when it is first created. In
      addition, it has been assigned an instance of the default
      SyncProvider implementation, RIOptimisticProvider.
    
      A SyncProvider object supplies a RowSetReader object (a
      reader) and a RowSetWriter object (a writer),
      which a disconnected RowSet object needs in order to read
      data from its data source or to write data back to its data
      source. What a reader and writer do is explained later in
      the sections What Reader Does and What Writer Does. One thing to keep in mind is that
      readers and writers work entirely in the background, so the
      explanation of how they work is for your information only.
      Having some background on readers and writers should help
      you understand what some of the methods defined in the
      CachedRowSet interface do in the background.
    
Generally, the default values for properties are fine as they are, but you may change the value of a property by calling the appropriate setter method. There are some properties without default values that you must set yourself.
      In order to get data, a disconnected RowSet object must
      be able to connect to a data source and have some means of
      selecting the data it is to hold. The following properties hold
      information necessary to obtain a connection to a database.
    
username: The name a user supplies to a
      database as part of gaining accesspassword: The user's database password
      url: The JDBC URL for the database to
      which the user wants to connectdatasourceName: The name used to
      retrieve a DataSource object that has been registered with
      a JNDI naming service
      
      Which of these
      properties you must set depends on how you are going to
      make a connection. The preferred way is to use a DataSource
      object, but it may not be practical for you to
      register a DataSource object with a JNDI naming service,
      which is generally done by a system administrator. Therefore, the code examples all
      use the DriverManager mechanism to obtain a connection, for
      which you use the url property and not the datasourceName
      property.
    
      The following lines of code set the username, password, and
      url properties so that a connection can be obtained using
      the DriverManager class. (You will find the JDBC URL to
      set as the value for the url property in the documentation
      for your JDBC driver.)
    
public void setConnectionProperties(String username, String password) {
  crs.setUsername(username);
  crs.setPassword(password);
  crs.setUrl("jdbc:mySubprotocol:mySubname");
  // ...
      Another property that you must set is the command property.
      In the reference implementation, data is read into a RowSet
      object from a ResultSet object. The query that produces
      that ResultSet object is the value for the command
      property. For example, the following line of code sets the
      command property with a query that produces a ResultSet
      object containing all the data in the table MERCH_INVENTORY:
    
crs.setCommand("select * from MERCH_INVENTORY");
      If you are going make any updates to the crs object and want
      those updates saved in the database, you must set one more
      piece of information: the key columns. Key columns are
      essentially the same as a primary key because they indicate
      one or more columns that uniquely identify a row. The
      difference is that a primary key is set on a table in the
      database, whereas key columns are set on a particular
      RowSet object. The following lines of code set the key
      columns for crs to the first column:
int [] keys = {1};
crs.setKeyColumns(keys);
      The first column in the table MERCH_INVENTORY is ITEM_ID. It can
      serve as the key column because every item identifier is different and therefore uniquely identifies one row and
      only one row in the table MERCH_INVENTORY. In addition, this column is specified as a primary key in the definition of the MERCH_INVENTORY table. The method setKeyColumns
      takes an array to allow for the fact that it may take two
      or more columns to identify a row uniquely.
    
      As a point of interest, the method setKeyColumns does not
      set a value for a property. In this case, it sets the value
      for the field keyCols. Key columns are used internally, so
      after setting them, you do nothing more with them. You will
      see how and when key columns are used in the section Using SyncResolver Objects.
    
Populating a disconnected RowSet object involves more work
      than populating a connected RowSet object. Fortunately, the extra work is done in the background. After you have
      done the preliminary work to set up the CachedRowSet object
      crs, the following line of code populates crs:
crs.execute();
      The data in crs is the data in the ResultSet object
      produced by executing the query in the command property.
    
      What is different is that the CachedRowSet implementation
      for the execute method does a lot more than the JdbcRowSet
      implementation. Or more correctly, the CachedRowSet
      object's reader, to which the method execute delegates its
      tasks, does a lot more.
    
      Every disconnected RowSet object has a SyncProvider object
      assigned to it, and this SyncProvider object is what
      provides the RowSet object's reader (a RowSetReader
      object). When the crs object was created, it was used as the default
      CachedRowSetImpl constructor, which, in addition to setting
      default values for properties, assigns an instance of the
      RIOptimisticProvider implementation as the default
      SyncProvider object.
    
      When an application calls the method execute, a
      disconnected RowSet object's reader works behind the scenes
      to populate the RowSet object with data. A newly created
      CachedRowSet object is not connected to a data source and
      therefore must obtain a connection to that data source in
      order to get data from it. The reference implementation of
      the default SyncProvider object (RIOptimisticProvider)
      provides a reader that obtains a connection by using the
      values set for the user name, password, and either the
      JDBC URL or the data source name, whichever was set more
      recently. Then the reader executes the query set for the
      command. It reads the data in the ResultSet object produced
      by the query, populating the CachedRowSet object with that
      data. Finally, the reader closes the connection.
    
      In the Coffee Break scenario, the owner wants to
      streamline operations. The owner decides to have employees at the
      warehouse enter inventory directly into a PDA (personal
      digital assistant), thereby avoiding the error-prone
      process of having a second person do the data entry. A
      CachedRowSet object is ideal in this situation because it
      is lightweight, serializable, and can be updated without a
      connection to the data source.
    
      The owner will have the application development team create a GUI tool for
      the PDA that warehouse employees will use for entering
      inventory data. Headquarters will create a CachedRowSet
      object populated with the table showing the current
      inventory and send it using the Internet to the PDAs. When a
      warehouse employee enters data using the GUI tool, the tool
      adds each entry to an array, which the CachedRowSet object
      will use to perform the updates in the background. Upon
      completion of the inventory, the PDAs send their new data
      back to headquarters, where the data is uploaded to the
      main server.
    
This section covers the following topics:
Updating data in a CachedRowSet object is just the same as
      updating data in a JdbcRowSet object. For example, the
      following code fragment from CachedRowSetSample.java increments the value in the column QUAN by 1 in the row whose ITEM_ID column has an item identifier of 12345:
    
        while (crs.next()) {
          System.out.println("Found item " + crs.getInt("ITEM_ID") + ": " +
                             crs.getString("ITEM_NAME"));
          if (crs.getInt("ITEM_ID") == 1235) {
            int currentQuantity = crs.getInt("QUAN") + 1;
            System.out.println("Updating quantity to " + currentQuantity);
            crs.updateInt("QUAN", currentQuantity + 1);
            crs.updateRow();
            // Synchronizing the row back to the DB
            crs.acceptChanges(con);
          }
      Just as with updating a column value, the code for
      inserting and deleting rows in a CachedRowSet object is the
      same as for a JdbcRowSet object.
    
The following excerpt from CachedRowSetSample.java inserts a new row into the CachedRowSet object crs:
        crs.moveToInsertRow();
        crs.updateInt("ITEM_ID", newItemId);
        crs.updateString("ITEM_NAME", "TableCloth");
        crs.updateInt("SUP_ID", 927);
        crs.updateInt("QUAN", 14);
        Calendar timeStamp;
        timeStamp = new GregorianCalendar();
        timeStamp.set(2006, 4, 1);
        crs.updateTimestamp("DATE_VAL", new Timestamp(timeStamp.getTimeInMillis()));
        crs.insertRow();
        crs.moveToCurrentRow();
      If headquarters has decided to stop stocking a particular item, it would
      probably remove the row for that coffee itself. However, in
      the scenario, a warehouse employee using a PDA also has the
      capability of removing it. The following code fragment
      finds the row where the value in the ITEM_ID column is
      12345 and deletes it from the CachedRowSet crs:
    
while (crs.next()) {
  if (crs.getInt("ITEM_ID") == 12345) {
    crs.deleteRow();
    break;
  }
}
There is a major difference between making changes to a
      JdbcRowSet object and making changes to a CachedRowSet
      object. Because a JdbcRowSet object is connected to its
      data source, the methods updateRow, insertRow, and
      deleteRow can update both the JdbcRowSet object and the
      data source. In the case of a disconnected RowSet object,
      however, these methods update the data stored in the
      CachedRowSet object's memory but cannot affect the data
      source. A disconnected RowSet object must call the method
      acceptChanges in order to save its changes to the data
      source. In the inventory scenario, back at headquarters, an
      application will call the method acceptChanges to update
      the database with the new values for the column QUAN.
    
crs.acceptChanges();
      Like the method execute, the method acceptChanges does its
      work invisibly. Whereas the method execute delegates its
      work to the RowSet object's reader, the method
      acceptChanges delegates its tasks to the RowSet object's
      writer. In the background, the writer opens a connection to
      the database, updates the database with the changes made to
      the RowSet object, and then closes the connection.
    
      The difficulty is that a conflict can arise. A
      conflict is a situation in which another party has updated
      a value in the database that corresponds to a value that
      was updated in a RowSet object. Which value should persist in the database? What the writer does when there
      is a conflict depends on how it is implemented, and there
      are many possibilities. At one end of the spectrum, the
      writer does not even check for conflicts and just writes
      all changes to the database. This is the case with the
      RIXMLProvider implementation, which is used by a WebRowSet
      object. At the other end, the writer ensures that there are
      no conflicts by setting database locks that prevent others
      from making changes.
    
      The writer for the crs object is the one provided by the
      default SyncProvider implementation, RIOptimisticProvider.
      The RIOPtimisticProvider implementation gets its name from
      the fact that it uses an optimistic concurrency model. This
      model assumes that there will be few, if any, conflicts and
      therefore sets no database locks. The writer checks to see
      if there are any conflicts, and if there is none, it
      writes the changes made to the crs object to the database, and those changes become persistent. If there are any conflicts, the default is not
      to write the new RowSet values to the database.
    
      In the scenario, the default behavior works very well.
      Because no one at headquarters is likely to change the
      value in the QUAN column of COF_INVENTORY, there will be no
      conflicts. As a result, the values entered into the crs object
      at the warehouse will be written to the database and thus will be
      persistent, which is the desired outcome.
    
      In other situations, however, it is possible for conflicts
      to exist. To accommodate these situations, the
      RIOPtimisticProvider implementation provides an option that
      lets you look at the values in conflict and decide which
      ones should be persistent. This option is the use of a SyncResolver
      object.
    
      When the writer has finished looking for conflicts and has
      found one or more, it creates a SyncResolver object
      containing the database values that caused the conflicts.
      Next, the method acceptChanges throws a
      SyncProviderException object, which an application may
      catch and use to retrieve the SyncResolver object. The
      following lines of code retrieve the SyncResolver object
      resolver:
    
try {
  crs.acceptChanges();
} catch (SyncProviderException spe) {
  SyncResolver resolver = spe.getSyncResolver();
}
The object resolver is a RowSet object that replicates
      the crs object except that it contains only the values in the
      database that caused a conflict. All other column values
      are null.
    
      With the resolver object, you can iterate through its
      rows to locate the values that are not null and are
      therefore values that caused a conflict. Then you can
      locate the value at the same position in the crs object and
      compare them. The following code fragment retrieves
      resolver and uses the SyncResolver method
      nextConflict to iterate through the rows that have conflicting
      values. The object resolver gets the status of each conflicting
      value, and if it is UPDATE_ROW_CONFLICT, meaning that the
      crs was attempting an update when the conflict
      occurred, the resolver object gets the row number of that
      value. Then the code moves the cursor for the crs object to the
      same row. Next, the code finds the column in that row of the
      resolver object that contains a conflicting value, which will
      be a value that is not null. After retrieving the value in
      that column from both the resolver and crs objects, you
      can compare the two and decide which one you want to become
      persistent. Finally, the code sets that value in both the
      crs object and the database using the method
      setResolvedValue, as shown in the following code:
    
try {
  crs.acceptChanges();
} catch (SyncProviderException spe) {
  SyncResolver resolver = spe.getSyncResolver();
  Object crsValue; // value in crs
  Object resolverValue; // value in the SyncResolver object
  Object resolvedValue; // value to be persistent
  while (resolver.nextConflict()) {
    if (resolver.getStatus() == SyncResolver.UPDATE_ROW_CONFLICT) {
      int row = resolver.getRow(); crs.absolute(row);
      int colCount = crs.getMetaData().getColumnCount();
      for (int j = 1; j <= colCount; j++) {
        if (resolver.getConflictValue(j) != null) {
          crsValue = crs.getObject(j);
          resolverValue = resolver.getConflictValue(j);
          // ...
          // compare crsValue and resolverValue to determine the
          // value to be persistent
          resolvedValue = crsValue; resolver.setResolvedValue(j, resolvedValue);
        }
      }
    }
  }
}
      Being a JavaBeans component means that a RowSet object can
      notify other components when certain things happen to it.
      For example, if data in a RowSet object changes, the RowSet
      object can notify interested parties of that change. The
      nice thing about this notification mechanism is that, as an
      application programmer, all you have to do is add or remove
      the components that will be notified.
    
This section covers the following topics:
      A listener for a RowSet object is a component that
      implements the following methods from the RowSetListener
      interface:
    
cursorMoved: Defines what the listener will do, if
      anything, when the cursor in the RowSet object moves.rowChanged: Defines what the listener will do, if
      anything, when one or more column values in a row have
      changed, a row has been inserted, or a row has been deleted.rowSetChanged: Defines what the listener will do, if
      anything, when the RowSet object has been populated with
      new data.
      An example of a component that might want to be a listener
      is a BarGraph object that graphs the data in a RowSet
      object. As the data changes, the BarGraph object can update
      itself to reflect the new data.
    
      As an application programmer, the only thing you must do
      to take advantage of the notification mechanism is to add
      or remove listeners. The following line of code means that
      every time the cursor for the crs objects moves, values in
      crs are changed, or crs as a whole gets new
      data, the BarGraph object bar will be notified:
    
crs.addRowSetListener(bar);
You can also stop notifications by removing a listener, as is done in the following line of code:
crs.removeRowSetListener(bar);
      Using the Coffee Break scenario, assume that
      headquarters checks with the database periodically to get
      the latest price list for the coffees it sells online. In
      this case, the listener is the PriceList object
      priceList at the Coffee Break web site, which must
      implement the RowSetListener methods cursorMoved,
      rowChanged, and rowSetChanged. The implementation of
      the cursorMoved method could be to do nothing because the position of
      the cursor does not affect the priceList object. The
      implementations for the rowChanged and rowSetChanged methods, on the other hand, must ascertain what changes have been made
      and update priceList accordingly.
In the reference implementation, methods that cause any of
      the RowSet events automatically notify all registered
      listeners. For example, any method that moves the cursor
      also calls the method cursorMoved on each of the listeners.
      Similarly, the method execute calls the method
      rowSetChanged on all listeners, and acceptChanges calls
      rowChanged on all listeners.
    
The sample code CachedRowSetSample.testCachedRowSet demonstrates how data can
		be sent in smaller pieces.