/* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.sql.rowset; import java.sql.*;import javax.sql.*;import javax.naming.*;import java.io.*;import java.math.*;import java.util.*; import javax.sql.rowset.spi.*; /** * The interface that all standard implementations of * <code>CachedRowSet</code> must implement. * <P> * The reference implementation of the <code>CachedRowSet</code> interface provided * by Oracle Corporation is a standard implementation. Developers may use this implementation * just as it is, they may extend it, or they may choose to write their own implementations * of this interface. * <P> * A <code>CachedRowSet</code> object is a container for rows of data * that caches its rows in memory, which makes it possible to operate without always being * connected to its data source. Further, it is a * JavaBeans<sup><font size=-2>TM</font></sup> component and is scrollable, * updatable, and serializable. A <code>CachedRowSet</code> object typically * contains rows from a result set, but it can also contain rows from any file * with a tabular format, such as a spread sheet. The reference implementation * supports getting data only from a <code>ResultSet</code> object, but * developers can extend the <code>SyncProvider</code> implementations to provide * access to other tabular data sources. * <P> * An application can modify the data in a <code>CachedRowSet</code> object, and * those modifications can then be propagated back to the source of the data. * <P> * A <code>CachedRowSet</code> object is a <i>disconnected</i> rowset, which means * that it makes use of a connection to its data source only briefly. It connects to its * data source while it is reading data to populate itself with rows and again * while it is propagating changes back to its underlying data source. The rest * of the time, a <code>CachedRowSet</code> object is disconnected, including * while its data is being modified. Being disconnected makes a <code>RowSet</code> * object much leaner and therefore much easier to pass to another component. For * example, a disconnected <code>RowSet</code> object can be serialized and passed * over the wire to a thin client such as a personal digital assistant (PDA). * <P> * * <h3>1.0 Creating a <code>CachedRowSet</code> Object</h3> * The following line of code uses the default constructor for * <code>CachedRowSet</code> * supplied in the reference implementation (RI) to create a default * <code>CachedRowSet</code> object. * <PRE> * CachedRowSetImpl crs = new CachedRowSetImpl(); * </PRE> * This new <code>CachedRowSet</code> object will have its properties set to the * default properties of a <code>BaseRowSet</code> object, and, in addition, it will * have an <code>RIOptimisticProvider</code> object as its synchronization provider. * <code>RIOptimisticProvider</code>, one of two <code>SyncProvider</code> * implementations included in the RI, is the default provider that the * <code>SyncFactory</code> singleton will supply when no synchronization * provider is specified. * <P> * A <code>SyncProvider</code> object provides a <code>CachedRowSet</code> object * with a reader (a <code>RowSetReader</code> object) for reading data from a * data source to populate itself with data. A reader can be implemented to read * data from a <code>ResultSet</code> object or from a file with a tabular format. * A <code>SyncProvider</code> object also provides * a writer (a <code>RowSetWriter</code> object) for synchronizing any * modifications to the <code>CachedRowSet</code> object's data made while it was * disconnected with the data in the underlying data source. * <P> * A writer can be implemented to exercise various degrees of care in checking * for conflicts and in avoiding them. * (A conflict occurs when a value in the data source has been changed after * the rowset populated itself with that value.) * The <code>RIOptimisticProvider</code> implementation assumes there will be * few or no conflicts and therefore sets no locks. It updates the data source * with values from the <code>CachedRowSet</code> object only if there are no * conflicts. * Other writers can be implemented so that they always write modified data to * the data source, which can be accomplished either by not checking for conflicts * or, on the other end of the spectrum, by setting locks sufficient to prevent data * in the data source from being changed. Still other writer implementations can be * somewhere in between. * <P> * A <code>CachedRowSet</code> object may use any * <code>SyncProvider</code> implementation that has been registered * with the <code>SyncFactory</code> singleton. An application * can find out which <code>SyncProvider</code> implementations have been * registered by calling the following line of code. * <PRE> * java.util.Enumeration providers = SyncFactory.getRegisteredProviders(); * </PRE> * <P> * There are two ways for a <code>CachedRowSet</code> object to specify which * <code>SyncProvider</code> object it will use. * <UL) * <LI>Supplying the name of the implementation to the constructor<BR> * The following line of code creates the <code>CachedRowSet</code> * object <i>crs2</i> that is initialized with default values except that its * <code>SyncProvider</code> object is the one specified. * <PRE> * CachedRowSetImpl crs2 = new CachedRowSetImpl( * "com.fred.providers.HighAvailabilityProvider"); * </PRE> * <LI>Setting the <code>SyncProvider</code> using the <code>CachedRowSet</code> * method <code>setSyncProvider</code><BR> * The following line of code resets the <code>SyncProvider</code> object * for <i>crs</i>, the <code>CachedRowSet</code> object created with the * default constructor. * <PRE> * crs.setSyncProvider("com.fred.providers.HighAvailabilityProvider"); * </PRE> * </UL) * See the comments for <code>SyncFactory</code> and <code>SyncProvider</code> for * more details. * * <P> * <h3>2.0 Retrieving Data from a <code>CachedRowSet</code> Object</h3> * Data is retrieved from a <code>CachedRowSet</code> object by using the * getter methods inherited from the <code>ResultSet</code> * interface. The following examples, in which <code>crs</code> is a * <code>CachedRowSet</code> * object, demonstrate how to iterate through the rows, retrieving the column * values in each row. The first example uses the version of the * getter methods that take a column number; the second example * uses the version that takes a column name. Column numbers are generally * used when the <code>RowSet</code> object's command * is of the form <code>SELECT * FROM TABLENAME</code>; column names are most * commonly used when the command specifies columns by name. * <PRE> * 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); * } * </PRE> * * <PRE> * 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); * } * </PRE> * <h4>2.1 Retrieving <code>RowSetMetaData</code></h4> * An application can get information about the columns in a <code>CachedRowSet</code> * object by calling <code>ResultSetMetaData</code> and <code>RowSetMetaData</code> * methods on a <code>RowSetMetaData</code> object. The following code fragment, * in which <i>crs</i> is a <code>CachedRowSet</code> object, illustrates the process. * The first line creates a <code>RowSetMetaData</code> object with information * about the columns in <i>crs</i>. The method <code>getMetaData</code>, * inherited from the <code>ResultSet</code> interface, returns a * <code>ResultSetMetaData</code> object, which is cast to a * <code>RowSetMetaData</code> object before being assigned to the variable * <i>rsmd</i>. The second line finds out how many columns <i>jrs</i> has, and * the third line gets the JDBC type of values stored in the second column of * <code>jrs</code>. * <PRE> * RowSetMetaData rsmd = (RowSetMetaData)crs.getMetaData(); * int count = rsmd.getColumnCount(); * int type = rsmd.getColumnType(2); * </PRE> * The <code>RowSetMetaData</code> interface differs from the * <code>ResultSetMetaData</code> interface in two ways. * <UL> * <LI><i>It includes <code>setter</code> methods:</i> A <code>RowSet</code> * object uses these methods internally when it is populated with data from a * different <code>ResultSet</code> object. * <P> * <LI><i>It contains fewer <code>getter</code> methods:</i> Some * <code>ResultSetMetaData</code> methods to not apply to a <code>RowSet</code> * object. For example, methods retrieving whether a column value is writable * or read only do not apply because all of a <code>RowSet</code> object's * columns will be writable or read only, depending on whether the rowset is * updatable or not. * </UL> * NOTE: In order to return a <code>RowSetMetaData</code> object, implementations must * override the <code>getMetaData()</code> method defined in * <code>java.sql.ResultSet</code> and return a <code>RowSetMetaData</code> object. * * <h3>3.0 Updating a <code>CachedRowSet</code> Object</h3> * Updating a <code>CachedRowSet</code> object is similar to updating a * <code>ResultSet</code> object, but because the rowset is not connected to * its data source while it is being updated, it must take an additional step * to effect changes in its underlying data source. After calling the method * <code>updateRow</code> or <code>insertRow</code>, a * <code>CachedRowSet</code> * object must also call the method <code>acceptChanges</code> to have updates * written to the data source. The following example, in which the cursor is * on a row in the <code>CachedRowSet</code> object <i>crs</i>, shows * the code required to update two column values in the current row and also * update the <code>RowSet</code> object's underlying data source. * <PRE> * crs.updateShort(3, 58); * crs.updateInt(4, 150000); * crs.updateRow(); * crs.acceptChanges(); * </PRE> * <P> * The next example demonstrates moving to the insert row, building a new * row on the insert row, inserting it into the rowset, and then calling the * method <code>acceptChanges</code> to add the new row to the underlying data * source. Note that as with the getter methods, the updater methods may take * either a column index or a column name to designate the column being acted upon. * <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> * NOTE: Where the <code>insertRow()</code> method inserts the contents of a * <code>CachedRowSet</code> object's insert row is implementation-defined. * The reference implementation for the <code>CachedRowSet</code> interface * inserts a new row immediately following the current row, but it could be * implemented to insert new rows in any number of other places. * <P> * Another thing to note about these examples is how they use the method * <code>acceptChanges</code>. It is this method that propagates changes in * a <code>CachedRowSet</code> object back to the underlying data source, * calling on the <code>RowSet</code> object's writer internally to write * changes to the data source. To do this, the writer has to incur the expense * of establishing a connection with that data source. The * preceding two code fragments call the method <code>acceptChanges</code> * immediately after calling <code>updateRow</code> or <code>insertRow</code>. * However, when there are multiple rows being changed, it is more efficient to call * <code>acceptChanges</code> after all calls to <code>updateRow</code> * and <code>insertRow</code> have been made. If <code>acceptChanges</code> * is called only once, only one connection needs to be established. * <P> * <h3>4.0 Updating the Underlying Data Source</h3> * When the method <code>acceptChanges</code> is executed, the * <code>CachedRowSet</code> object's writer, a <code>RowSetWriterImpl</code> * object, is called behind the scenes to write the changes made to the * rowset to the underlying data source. The writer is implemented to make a * connection to the data source and write updates to it. * <P> * A writer is made available through an implementation of the * <code>SyncProvider</code> interface, as discussed in section 1, * "Creating a <code>CachedRowSet</code> Object." * The default reference implementation provider, <code>RIOptimisticProvider</code>, * has its writer implemented to use an optimistic concurrency control * mechanism. That is, it maintains no locks in the underlying database while * the rowset is disconnected from the database and simply checks to see if there * are any conflicts before writing data to the data source. If there are any * conflicts, it does not write anything to the data source. * <P> * The reader/writer facility * provided by the <code>SyncProvider</code> class is pluggable, allowing for the * customization of data retrieval and updating. If a different concurrency * control mechanism is desired, a different implementation of * <code>SyncProvider</code> can be plugged in using the method * <code>setSyncProvider</code>. * <P> * In order to use the optimistic concurrency control routine, the * <code>RIOptismisticProvider</code> maintains both its current * value and its original value (the value it had immediately preceding the * current value). Note that if no changes have been made to the data in a * <code>RowSet</code> object, its current values and its original values are the same, * both being the values with which the <code>RowSet</code> object was initially * populated. However, once any values in the <code>RowSet</code> object have been * changed, the current values and the original values will be different, though at * this stage, the original values are still the initial values. With any subsequent * changes to data in a <code>RowSet</code> object, its original values and current * values will still differ, but its original values will be the values that * were previously the current values. * <P> * Keeping track of original values allows the writer to compare the <code>RowSet</code> * object's original value with the value in the database. If the values in * the database differ from the <code>RowSet</code> object's original values, which means that * the values in the database have been changed, there is a conflict. * Whether a writer checks for conflicts, what degree of checking it does, and how * it handles conflicts all depend on how it is implemented. * <P> * <h3>5.0 Registering and Notifying Listeners</h3> * Being JavaBeans components, all rowsets participate in the JavaBeans event * model, inheriting methods for registering listeners and notifying them of * changes from the <code>BaseRowSet</code> class. A listener for a * <code>CachedRowSet</code> object is a component that wants to be notified * whenever there is a change in the rowset. For example, if a * <code>CachedRowSet</code> object contains the results of a query and * those * results are being displayed in, say, a table and a bar graph, the table and * bar graph could be registered as listeners with the rowset so that they can * update themselves to reflect changes. To become listeners, the table and * bar graph classes must implement the <code>RowSetListener</code> interface. * Then they can be added to the <Code>CachedRowSet</code> object's list of * listeners, as is illustrated in the following lines of code. * <PRE> * crs.addRowSetListener(table); * crs.addRowSetListener(barGraph); * </PRE> * Each <code>CachedRowSet</code> method that moves the cursor or changes * data also notifies registered listeners of the changes, so * <code>table</code> and <code>barGraph</code> will be notified when there is * a change in <code>crs</code>. * <P> * <h3>6.0 Passing Data to Thin Clients</h3> * One of the main reasons to use a <code>CachedRowSet</code> object is to * pass data between different components of an application. Because it is * serializable, a <code>CachedRowSet</code> object can be used, for example, * to send the result of a query executed by an enterprise JavaBeans component * running in a server environment over a network to a client running in a * web browser. * <P> * While a <code>CachedRowSet</code> object is disconnected, it can be much * leaner than a <code>ResultSet</code> object with the same data. * As a result, it can be especially suitable for sending data to a thin client * such as a PDA, where it would be inappropriate to use a JDBC driver * due to resource limitations or security considerations. * Thus, a <code>CachedRowSet</code> object provides a means to "get rows in" * without the need to implement the full JDBC API. * <P> * <h3>7.0 Scrolling and Updating</h3> * A second major use for <code>CachedRowSet</code> objects is to provide * scrolling and updating for <code>ResultSet</code> objects that * do not provide these capabilities themselves. In other words, a * <code>CachedRowSet</code> object can be used to augment the * capabilities of a JDBC technology-enabled driver (hereafter called a * "JDBC driver") when the DBMS does not provide full support for scrolling and * updating. To achieve the effect of making a non-scrollble and read-only * <code>ResultSet</code> object scrollable and updatable, a programmer * simply needs to create a <code>CachedRowSet</code> object populated * with that <code>ResultSet</code> object's data. This is demonstrated * in the following code fragment, where <code>stmt</code> is a * <code>Statement</code> object. * <PRE> * ResultSet rs = stmt.executeQuery("SELECT * FROM EMPLOYEES"); * CachedRowSetImpl crs = new CachedRowSetImpl(); * crs.populate(rs); * </PRE> * <P> * The object <code>crs</code> now contains the data from the table * <code>EMPLOYEES</code>, just as the object <code>rs</code> does. * The difference is that the cursor for <code>crs</code> can be moved * forward, backward, or to a particular row even if the cursor for * <code>rs</code> can move only forward. In addition, <code>crs</code> is * updatable even if <code>rs</code> is not because by default, a * <code>CachedRowSet</code> object is both scrollable and updatable. * <P> * In summary, a <code>CachedRowSet</code> object can be thought of as simply * a disconnected set of rows that are being cached outside of a data source. * Being thin and serializable, it can easily be sent across a wire, * and it is well suited to sending data to a thin client. However, a * <code>CachedRowSet</code> object does have a limitation: It is limited in * size by the amount of data it can store in memory at one time. * <P> * <h3>8.0 Getting Universal Data Access</h3> * Another advantage of the <code>CachedRowSet</code> class is that it makes it * possible to retrieve and store data from sources other than a relational * database. The reader for a rowset can be implemented to read and populate * its rowset with data from any tabular data source, including a spreadsheet * or flat file. * Because both a <code>CachedRowSet</code> object and its metadata can be * created from scratch, a component that acts as a factory for rowsets * can use this capability to create a rowset containing data from * non-SQL data sources. Nevertheless, it is expected that most of the time, * <code>CachedRowSet</code> objects will contain data that was fetched * from an SQL database using the JDBC API. * <P> * <h3>9.0 Setting Properties</h3> * All rowsets maintain a set of properties, which will usually be set using * a tool. The number and kinds of properties a rowset has will vary, * depending on what the rowset does and how it gets its data. For example, * rowsets that get their data from a <code>ResultSet</code> object need to * set the properties that are required for making a database connection. * If a rowset uses the <code>DriverManager</code> facility to make a * connection, it needs to set a property for the JDBC URL that identifies * the appropriate driver, and it needs to set the properties that give the * user name and password. * If, on the other hand, the rowset uses a <code>DataSource</code> object * to make the connection, which is the preferred method, it does not need to * set the property for the JDBC URL. Instead, it needs to set * properties for the logical name of the data source, for the user name, * and for the password. * <P> * NOTE: In order to use a <code>DataSource</code> object for making a * connection, the <code>DataSource</code> object must have been registered * with a naming service that uses the Java Naming and Directory * Interface<sup><font size=-2>TM</font></sup> (JNDI) API. This registration * is usually done by a person acting in the capacity of a system * administrator. * <P> * In order to be able to populate itself with data from a database, a rowset * needs to set a command property. This property is a query that is a * <code>PreparedStatement</code> object, which allows the query to have * parameter placeholders that are set at run time, as opposed to design time. * To set these placeholder parameters with values, a rowset provides * setter methods for setting values of each data type, * similar to the setter methods provided by the <code>PreparedStatement</code> * interface. * <P> * The following code fragment illustrates how the <code>CachedRowSet</code> * object <code>crs</code> might have its command property set. Note that if a * tool is used to set properties, this is the code that the tool would use. * <PRE> * crs.setCommand("SELECT FIRST_NAME, LAST_NAME, ADDRESS FROM CUSTOMERS " + * "WHERE CREDIT_LIMIT > ? AND REGION = ?"); * </PRE> * <P> * The values that will be used to set the command's placeholder parameters are * contained in the <code>RowSet</code> object's <code>params</code> field, which is a * <code>Vector</code> object. * The <code>CachedRowSet</code> class provides a set of setter * methods for setting the elements in its <code>params</code> field. The * following code fragment demonstrates setting the two parameters in the * query from the previous example. * <PRE> * crs.setInt(1, 5000); * crs.setString(2, "West"); * </PRE> * <P> * The <code>params</code> field now contains two elements, each of which is * an array two elements long. The first element is the parameter number; * the second is the value to be set. * In this case, the first element of <code>params</code> is * <code>1</code>, <code>5000</code>, and the second element is <code>2</code>, * <code>"West"</code>. When an application calls the method * <code>execute</code>, it will in turn call on this <code>RowSet</code> object's reader, * which will in turn invoke its <code>readData</code> method. As part of * its implementation, <code>readData</code> will get the values in * <code>params</code> and use them to set the command's placeholder * parameters. * The following code fragment gives an idea of how the reader * does this, after obtaining the <code>Connection</code> object * <code>con</code>. * <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> * At this point, the command for <code>crs</code> is the query <code>"SELECT * FIRST_NAME, LAST_NAME, ADDRESS FROM CUSTOMERS WHERE CREDIT_LIMIT > 5000 * AND REGION = "West"</code>. After the <code>readData</code> method executes * this command with the following line of code, it will have the data from * <code>rs</code> with which to populate <code>crs</code>. * <PRE> * ResultSet rs = pstmt.executeQuery(); * </PRE> * <P> * The preceding code fragments give an idea of what goes on behind the * scenes; they would not appear in an application, which would not invoke * methods like <code>readData</code> and <code>decodeParams</code>. * In contrast, the following code fragment shows what an application might do. * It sets the rowset's command, sets the command's parameters, and executes * the command. Simply by calling the <code>execute</code> method, * <code>crs</code> populates itself with the requested data from the * table <code>CUSTOMERS</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 Paging Data</h3> * Because a <code>CachedRowSet</code> object stores data in memory, * the amount of data that it can contain at any one * time is determined by the amount of memory available. To get around this limitation, * a <code>CachedRowSet</code> object can retrieve data from a <code>ResultSet</code> * object in chunks of data, called <i>pages</i>. To take advantage of this mechanism,Lines 1–500 of 1625. Load more
/* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.sql.rowset; import java.sql.*; import javax.sql.*; import javax.naming.*; import java.io.*; import java.math.*; import java.util.*; import javax.sql.rowset.spi.*; /** * The interface that all standard implementations of * <code>CachedRowSet</code> must implement. * <P> * The reference implementation of the <code>CachedRowSet</code> interface provided * by Oracle Corporation is a standard implementation. Developers may use this implementation * just as it is, they may extend it, or they may choose to write their own implementations * of this interface. * <P> * A <code>CachedRowSet</code> object is a container for rows of data * that caches its rows in memory, which makes it possible to operate without always being * connected to its data source. Further, it is a * JavaBeans<sup><font size=-2>TM</font></sup> component and is scrollable, * updatable, and serializable. A <code>CachedRowSet</code> object typically * contains rows from a result set, but it can also contain rows from any file * with a tabular format, such as a spread sheet. The reference implementation * supports getting data only from a <code>ResultSet</code> object, but * developers can extend the <code>SyncProvider</code> implementations to provide * access to other tabular data sources. * <P> * An application can modify the data in a <code>CachedRowSet</code> object, and * those modifications can then be propagated back to the source of the data. * <P> * A <code>CachedRowSet</code> object is a <i>disconnected</i> rowset, which means * that it makes use of a connection to its data source only briefly. It connects to its * data source while it is reading data to populate itself with rows and again * while it is propagating changes back to its underlying data source. The rest * of the time, a <code>CachedRowSet</code> object is disconnected, including * while its data is being modified. Being disconnected makes a <code>RowSet</code> * object much leaner and therefore much easier to pass to another component. For * example, a disconnected <code>RowSet</code> object can be serialized and passed * over the wire to a thin client such as a personal digital assistant (PDA). * <P> * * <h3>1.0 Creating a <code>CachedRowSet</code> Object</h3> * The following line of code uses the default constructor for * <code>CachedRowSet</code> * supplied in the reference implementation (RI) to create a default * <code>CachedRowSet</code> object. * <PRE> * CachedRowSetImpl crs = new CachedRowSetImpl(); * </PRE> * This new <code>CachedRowSet</code> object will have its properties set to the * default properties of a <code>BaseRowSet</code> object, and, in addition, it will * have an <code>RIOptimisticProvider</code> object as its synchronization provider. * <code>RIOptimisticProvider</code>, one of two <code>SyncProvider</code> * implementations included in the RI, is the default provider that the * <code>SyncFactory</code> singleton will supply when no synchronization * provider is specified. * <P> * A <code>SyncProvider</code> object provides a <code>CachedRowSet</code> object * with a reader (a <code>RowSetReader</code> object) for reading data from a * data source to populate itself with data. A reader can be implemented to read * data from a <code>ResultSet</code> object or from a file with a tabular format. * A <code>SyncProvider</code> object also provides * a writer (a <code>RowSetWriter</code> object) for synchronizing any * modifications to the <code>CachedRowSet</code> object's data made while it was * disconnected with the data in the underlying data source. * <P> * A writer can be implemented to exercise various degrees of care in checking * for conflicts and in avoiding them. * (A conflict occurs when a value in the data source has been changed after * the rowset populated itself with that value.) * The <code>RIOptimisticProvider</code> implementation assumes there will be * few or no conflicts and therefore sets no locks. It updates the data source * with values from the <code>CachedRowSet</code> object only if there are no * conflicts. * Other writers can be implemented so that they always write modified data to * the data source, which can be accomplished either by not checking for conflicts * or, on the other end of the spectrum, by setting locks sufficient to prevent data * in the data source from being changed. Still other writer implementations can be * somewhere in between. * <P> * A <code>CachedRowSet</code> object may use any * <code>SyncProvider</code> implementation that has been registered * with the <code>SyncFactory</code> singleton. An application * can find out which <code>SyncProvider</code> implementations have been * registered by calling the following line of code. * <PRE> * java.util.Enumeration providers = SyncFactory.getRegisteredProviders(); * </PRE> * <P> * There are two ways for a <code>CachedRowSet</code> object to specify which * <code>SyncProvider</code> object it will use. * <UL) * <LI>Supplying the name of the implementation to the constructor<BR> * The following line of code creates the <code>CachedRowSet</code> * object <i>crs2</i> that is initialized with default values except that its * <code>SyncProvider</code> object is the one specified. * <PRE> * CachedRowSetImpl crs2 = new CachedRowSetImpl( * "com.fred.providers.HighAvailabilityProvider"); * </PRE> * <LI>Setting the <code>SyncProvider</code> using the <code>CachedRowSet</code> * method <code>setSyncProvider</code><BR> * The following line of code resets the <code>SyncProvider</code> object * for <i>crs</i>, the <code>CachedRowSet</code> object created with the * default constructor. * <PRE> * crs.setSyncProvider("com.fred.providers.HighAvailabilityProvider"); * </PRE> * </UL) * See the comments for <code>SyncFactory</code> and <code>SyncProvider</code> for * more details. * * <P> * <h3>2.0 Retrieving Data from a <code>CachedRowSet</code> Object</h3> * Data is retrieved from a <code>CachedRowSet</code> object by using the * getter methods inherited from the <code>ResultSet</code> * interface. The following examples, in which <code>crs</code> is a * <code>CachedRowSet</code> * object, demonstrate how to iterate through the rows, retrieving the column * values in each row. The first example uses the version of the * getter methods that take a column number; the second example * uses the version that takes a column name. Column numbers are generally * used when the <code>RowSet</code> object's command * is of the form <code>SELECT * FROM TABLENAME</code>; column names are most * commonly used when the command specifies columns by name. * <PRE> * 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); * } * </PRE> * * <PRE> * 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); * } * </PRE> * <h4>2.1 Retrieving <code>RowSetMetaData</code></h4> * An application can get information about the columns in a <code>CachedRowSet</code> * object by calling <code>ResultSetMetaData</code> and <code>RowSetMetaData</code> * methods on a <code>RowSetMetaData</code> object. The following code fragment, * in which <i>crs</i> is a <code>CachedRowSet</code> object, illustrates the process. * The first line creates a <code>RowSetMetaData</code> object with information * about the columns in <i>crs</i>. The method <code>getMetaData</code>, * inherited from the <code>ResultSet</code> interface, returns a * <code>ResultSetMetaData</code> object, which is cast to a * <code>RowSetMetaData</code> object before being assigned to the variable * <i>rsmd</i>. The second line finds out how many columns <i>jrs</i> has, and * the third line gets the JDBC type of values stored in the second column of * <code>jrs</code>. * <PRE> * RowSetMetaData rsmd = (RowSetMetaData)crs.getMetaData(); * int count = rsmd.getColumnCount(); * int type = rsmd.getColumnType(2); * </PRE> * The <code>RowSetMetaData</code> interface differs from the * <code>ResultSetMetaData</code> interface in two ways. * <UL> * <LI><i>It includes <code>setter</code> methods:</i> A <code>RowSet</code> * object uses these methods internally when it is populated with data from a * different <code>ResultSet</code> object. * <P> * <LI><i>It contains fewer <code>getter</code> methods:</i> Some * <code>ResultSetMetaData</code> methods to not apply to a <code>RowSet</code> * object. For example, methods retrieving whether a column value is writable * or read only do not apply because all of a <code>RowSet</code> object's * columns will be writable or read only, depending on whether the rowset is * updatable or not. * </UL> * NOTE: In order to return a <code>RowSetMetaData</code> object, implementations must * override the <code>getMetaData()</code> method defined in * <code>java.sql.ResultSet</code> and return a <code>RowSetMetaData</code> object. * * <h3>3.0 Updating a <code>CachedRowSet</code> Object</h3> * Updating a <code>CachedRowSet</code> object is similar to updating a * <code>ResultSet</code> object, but because the rowset is not connected to * its data source while it is being updated, it must take an additional step * to effect changes in its underlying data source. After calling the method * <code>updateRow</code> or <code>insertRow</code>, a * <code>CachedRowSet</code> * object must also call the method <code>acceptChanges</code> to have updates * written to the data source. The following example, in which the cursor is * on a row in the <code>CachedRowSet</code> object <i>crs</i>, shows * the code required to update two column values in the current row and also * update the <code>RowSet</code> object's underlying data source. * <PRE> * crs.updateShort(3, 58); * crs.updateInt(4, 150000); * crs.updateRow(); * crs.acceptChanges(); * </PRE> * <P> * The next example demonstrates moving to the insert row, building a new * row on the insert row, inserting it into the rowset, and then calling the * method <code>acceptChanges</code> to add the new row to the underlying data * source. Note that as with the getter methods, the updater methods may take * either a column index or a column name to designate the column being acted upon. * <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> * NOTE: Where the <code>insertRow()</code> method inserts the contents of a * <code>CachedRowSet</code> object's insert row is implementation-defined. * The reference implementation for the <code>CachedRowSet</code> interface * inserts a new row immediately following the current row, but it could be * implemented to insert new rows in any number of other places. * <P> * Another thing to note about these examples is how they use the method * <code>acceptChanges</code>. It is this method that propagates changes in * a <code>CachedRowSet</code> object back to the underlying data source, * calling on the <code>RowSet</code> object's writer internally to write * changes to the data source. To do this, the writer has to incur the expense * of establishing a connection with that data source. The * preceding two code fragments call the method <code>acceptChanges</code> * immediately after calling <code>updateRow</code> or <code>insertRow</code>. * However, when there are multiple rows being changed, it is more efficient to call * <code>acceptChanges</code> after all calls to <code>updateRow</code> * and <code>insertRow</code> have been made. If <code>acceptChanges</code> * is called only once, only one connection needs to be established. * <P> * <h3>4.0 Updating the Underlying Data Source</h3> * When the method <code>acceptChanges</code> is executed, the * <code>CachedRowSet</code> object's writer, a <code>RowSetWriterImpl</code> * object, is called behind the scenes to write the changes made to the * rowset to the underlying data source. The writer is implemented to make a * connection to the data source and write updates to it. * <P> * A writer is made available through an implementation of the * <code>SyncProvider</code> interface, as discussed in section 1, * "Creating a <code>CachedRowSet</code> Object." * The default reference implementation provider, <code>RIOptimisticProvider</code>, * has its writer implemented to use an optimistic concurrency control * mechanism. That is, it maintains no locks in the underlying database while * the rowset is disconnected from the database and simply checks to see if there * are any conflicts before writing data to the data source. If there are any * conflicts, it does not write anything to the data source. * <P> * The reader/writer facility * provided by the <code>SyncProvider</code> class is pluggable, allowing for the * customization of data retrieval and updating. If a different concurrency * control mechanism is desired, a different implementation of * <code>SyncProvider</code> can be plugged in using the method * <code>setSyncProvider</code>. * <P> * In order to use the optimistic concurrency control routine, the * <code>RIOptismisticProvider</code> maintains both its current * value and its original value (the value it had immediately preceding the * current value). Note that if no changes have been made to the data in a * <code>RowSet</code> object, its current values and its original values are the same, * both being the values with which the <code>RowSet</code> object was initially * populated. However, once any values in the <code>RowSet</code> object have been * changed, the current values and the original values will be different, though at * this stage, the original values are still the initial values. With any subsequent * changes to data in a <code>RowSet</code> object, its original values and current * values will still differ, but its original values will be the values that * were previously the current values. * <P> * Keeping track of original values allows the writer to compare the <code>RowSet</code> * object's original value with the value in the database. If the values in * the database differ from the <code>RowSet</code> object's original values, which means that * the values in the database have been changed, there is a conflict. * Whether a writer checks for conflicts, what degree of checking it does, and how * it handles conflicts all depend on how it is implemented. * <P> * <h3>5.0 Registering and Notifying Listeners</h3> * Being JavaBeans components, all rowsets participate in the JavaBeans event * model, inheriting methods for registering listeners and notifying them of * changes from the <code>BaseRowSet</code> class. A listener for a * <code>CachedRowSet</code> object is a component that wants to be notified * whenever there is a change in the rowset. For example, if a * <code>CachedRowSet</code> object contains the results of a query and * those * results are being displayed in, say, a table and a bar graph, the table and * bar graph could be registered as listeners with the rowset so that they can * update themselves to reflect changes. To become listeners, the table and * bar graph classes must implement the <code>RowSetListener</code> interface. * Then they can be added to the <Code>CachedRowSet</code> object's list of * listeners, as is illustrated in the following lines of code. * <PRE> * crs.addRowSetListener(table); * crs.addRowSetListener(barGraph); * </PRE> * Each <code>CachedRowSet</code> method that moves the cursor or changes * data also notifies registered listeners of the changes, so * <code>table</code> and <code>barGraph</code> will be notified when there is * a change in <code>crs</code>. * <P> * <h3>6.0 Passing Data to Thin Clients</h3> * One of the main reasons to use a <code>CachedRowSet</code> object is to * pass data between different components of an application. Because it is * serializable, a <code>CachedRowSet</code> object can be used, for example, * to send the result of a query executed by an enterprise JavaBeans component * running in a server environment over a network to a client running in a * web browser. * <P> * While a <code>CachedRowSet</code> object is disconnected, it can be much * leaner than a <code>ResultSet</code> object with the same data. * As a result, it can be especially suitable for sending data to a thin client * such as a PDA, where it would be inappropriate to use a JDBC driver * due to resource limitations or security considerations. * Thus, a <code>CachedRowSet</code> object provides a means to "get rows in" * without the need to implement the full JDBC API. * <P> * <h3>7.0 Scrolling and Updating</h3> * A second major use for <code>CachedRowSet</code> objects is to provide * scrolling and updating for <code>ResultSet</code> objects that * do not provide these capabilities themselves. In other words, a * <code>CachedRowSet</code> object can be used to augment the * capabilities of a JDBC technology-enabled driver (hereafter called a * "JDBC driver") when the DBMS does not provide full support for scrolling and * updating. To achieve the effect of making a non-scrollble and read-only * <code>ResultSet</code> object scrollable and updatable, a programmer * simply needs to create a <code>CachedRowSet</code> object populated * with that <code>ResultSet</code> object's data. This is demonstrated * in the following code fragment, where <code>stmt</code> is a * <code>Statement</code> object. * <PRE> * ResultSet rs = stmt.executeQuery("SELECT * FROM EMPLOYEES"); * CachedRowSetImpl crs = new CachedRowSetImpl(); * crs.populate(rs); * </PRE> * <P> * The object <code>crs</code> now contains the data from the table * <code>EMPLOYEES</code>, just as the object <code>rs</code> does. * The difference is that the cursor for <code>crs</code> can be moved * forward, backward, or to a particular row even if the cursor for * <code>rs</code> can move only forward. In addition, <code>crs</code> is * updatable even if <code>rs</code> is not because by default, a * <code>CachedRowSet</code> object is both scrollable and updatable. * <P> * In summary, a <code>CachedRowSet</code> object can be thought of as simply * a disconnected set of rows that are being cached outside of a data source. * Being thin and serializable, it can easily be sent across a wire, * and it is well suited to sending data to a thin client. However, a * <code>CachedRowSet</code> object does have a limitation: It is limited in * size by the amount of data it can store in memory at one time. * <P> * <h3>8.0 Getting Universal Data Access</h3> * Another advantage of the <code>CachedRowSet</code> class is that it makes it * possible to retrieve and store data from sources other than a relational * database. The reader for a rowset can be implemented to read and populate * its rowset with data from any tabular data source, including a spreadsheet * or flat file. * Because both a <code>CachedRowSet</code> object and its metadata can be * created from scratch, a component that acts as a factory for rowsets * can use this capability to create a rowset containing data from * non-SQL data sources. Nevertheless, it is expected that most of the time, * <code>CachedRowSet</code> objects will contain data that was fetched * from an SQL database using the JDBC API. * <P> * <h3>9.0 Setting Properties</h3> * All rowsets maintain a set of properties, which will usually be set using * a tool. The number and kinds of properties a rowset has will vary, * depending on what the rowset does and how it gets its data. For example, * rowsets that get their data from a <code>ResultSet</code> object need to * set the properties that are required for making a database connection. * If a rowset uses the <code>DriverManager</code> facility to make a * connection, it needs to set a property for the JDBC URL that identifies * the appropriate driver, and it needs to set the properties that give the * user name and password. * If, on the other hand, the rowset uses a <code>DataSource</code> object * to make the connection, which is the preferred method, it does not need to * set the property for the JDBC URL. Instead, it needs to set * properties for the logical name of the data source, for the user name, * and for the password. * <P> * NOTE: In order to use a <code>DataSource</code> object for making a * connection, the <code>DataSource</code> object must have been registered * with a naming service that uses the Java Naming and Directory * Interface<sup><font size=-2>TM</font></sup> (JNDI) API. This registration * is usually done by a person acting in the capacity of a system * administrator. * <P> * In order to be able to populate itself with data from a database, a rowset * needs to set a command property. This property is a query that is a * <code>PreparedStatement</code> object, which allows the query to have * parameter placeholders that are set at run time, as opposed to design time. * To set these placeholder parameters with values, a rowset provides * setter methods for setting values of each data type, * similar to the setter methods provided by the <code>PreparedStatement</code> * interface. * <P> * The following code fragment illustrates how the <code>CachedRowSet</code> * object <code>crs</code> might have its command property set. Note that if a * tool is used to set properties, this is the code that the tool would use. * <PRE> * crs.setCommand("SELECT FIRST_NAME, LAST_NAME, ADDRESS FROM CUSTOMERS " + * "WHERE CREDIT_LIMIT > ? AND REGION = ?"); * </PRE> * <P> * The values that will be used to set the command's placeholder parameters are * contained in the <code>RowSet</code> object's <code>params</code> field, which is a * <code>Vector</code> object. * The <code>CachedRowSet</code> class provides a set of setter * methods for setting the elements in its <code>params</code> field. The * following code fragment demonstrates setting the two parameters in the * query from the previous example. * <PRE> * crs.setInt(1, 5000); * crs.setString(2, "West"); * </PRE> * <P> * The <code>params</code> field now contains two elements, each of which is * an array two elements long. The first element is the parameter number; * the second is the value to be set. * In this case, the first element of <code>params</code> is * <code>1</code>, <code>5000</code>, and the second element is <code>2</code>, * <code>"West"</code>. When an application calls the method * <code>execute</code>, it will in turn call on this <code>RowSet</code> object's reader, * which will in turn invoke its <code>readData</code> method. As part of * its implementation, <code>readData</code> will get the values in * <code>params</code> and use them to set the command's placeholder * parameters. * The following code fragment gives an idea of how the reader * does this, after obtaining the <code>Connection</code> object * <code>con</code>. * <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> * At this point, the command for <code>crs</code> is the query <code>"SELECT * FIRST_NAME, LAST_NAME, ADDRESS FROM CUSTOMERS WHERE CREDIT_LIMIT > 5000 * AND REGION = "West"</code>. After the <code>readData</code> method executes * this command with the following line of code, it will have the data from * <code>rs</code> with which to populate <code>crs</code>. * <PRE> * ResultSet rs = pstmt.executeQuery(); * </PRE> * <P> * The preceding code fragments give an idea of what goes on behind the * scenes; they would not appear in an application, which would not invoke * methods like <code>readData</code> and <code>decodeParams</code>. * In contrast, the following code fragment shows what an application might do. * It sets the rowset's command, sets the command's parameters, and executes * the command. Simply by calling the <code>execute</code> method, * <code>crs</code> populates itself with the requested data from the * table <code>CUSTOMERS</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 Paging Data</h3> * Because a <code>CachedRowSet</code> object stores data in memory, * the amount of data that it can contain at any one * time is determined by the amount of memory available. To get around this limitation, * a <code>CachedRowSet</code> object can retrieve data from a <code>ResultSet</code> * object in chunks of data, called <i>pages</i>. To take advantage of this mechanism,