Documentation Contents |
RowSet
object is a container
for tabular data, encapsulating a set of zero or more rows that
have been retrieved from a data source. In a basic implementation
of the RowSet
interface, the rows are retrieved from a
JDBC data source, but a rowset may be customized so that its data
can also be from a spreadsheet, a flat file, or any other data
source with a tabular format. A RowSet
object extends
the ResultSet
interface, which means that it can be
scrollable, can be updatable, and can do anything a
ResultSet
object can do. The features of a
RowSet
object, which are summarized in this
introductory section, will be explained in more detail in later
sections.
A RowSet
object differs from
a ResultSet
object in that it is a
JavaBeansTM component. Thus, it
has a set of JavaBeans properties and follows the JavaBeans event
model. A RowSet
object's properties allow it to
establish its own database connection and to execute its own query
in order to fill itself with data. A rowset may be
disconnected, that is, function without maintaining an
open connection to a data source the whole time it is in use. In
addition, a rowset can be serialized, which means that it can be
sent to a remote object over a network.
In general, the JDBC API can be divided
into two categories, the RowSet
portion and the driver
portion. RowSet
and its supporting interfaces are
intended to be implemented using the rest of the JDBC API. In other
words, a class that implements the RowSet
interface is
a layer of software that is said to execute "on top" of a JDBC
driver. Unlike other JDBC objects, a RowSet
object
contains within itself the means to operate without a driver and
without being connected to a data source.
The release of J2SE 5.0 introduced a
third category. In addition to the RowSet
API and the
driver, there are five standard implementations of the
RowSet
interface. These implementations provide a set
of interfaces that extend the basic RowSet
interface
plus reference implementations for each of them. There is no
requirement to use these implementations, but by using them,
developers can be sure that their implementations follow the JDBC
API in event handling, cursor manipulation, and other operations.
The standard implementations are discussed more fully in "Standard Implementations," on
page 808.
The RowSet
interface provides
a basic set of methods common to all rowsets, which this section
describes. All RowSet
objects are JavaBeans
components; therefore, the RowSet
interface has
methods for adding and removing an event listener, and it has
getter amd setter methods for all of its properties. Many of these
properties support setting up a connection or executing a command.
A rowset uses a connection with a data source in order to execute a
query and produce a result set from which it will get its data. It
may also use a connection to write modified data back to the data
source. In addition, the RowSet
interface has methods
(one for each data type) for setting the values of input
parameters, if any, in a RowSet
object's command
string. In the JDBC RowSet
Implementations
specification, these basic methods, defined in the
RowSet
interface, are provided in the
BaseRowSet
abstract class, which is discussed
later.
Five other interfaces and one class work
together with the RowSet
interface behind the scenes.
The class RowSetEvent
and the interface
RowSetListener
support the JavaBeans event model. When
a RowSet
object's cursor moves or its data is
modified, it will invoke the RowSetListener
method
corresponding to the event, providing it with a
RowSetEvent
object that identifies itself as the
source of the event. Implementations are free to write extensions
that add other RowSet
events if they are needed.
A component that wants to be notified of
the events that occur in a RowSet
object will
implement the RowSetListener
interface and be
registered with the RowSet
object. Such a component,
called a listener, is typically a GUI (graphical user
interface) component, such as a table or bar chart, that is
displaying the RowSet
object's data. Because a
listener is notified every time an event occurs in the rowset, it
can keep its cursor position and data consistent with that of the
rowset.
The interfaces
RowSetInternal
, RowSetReader
, and
RowSetWriter
support the rowset reader/writer
facility. A reader, an instance of a class that implements
the RowSetReader
interface, reads data and inserts it
into a rowset. A writer, an instance of a class that
implements the RowSetWriter
interface, writes modified
data back to the data source from which a rowset's data was
retrieved.
The RowSetInternal
interface
provides additional methods for a reader or writer to use to
manipulate the rowset's internal state. For example, a rowset can
keep track of its original values, and RowSetInternal
methods allow the writer to see if the corresponding data in the
data source has been changed by someone else. In addition,
RowSetInternal
methods make it possible to retrieve
the input parameters that were set for a rowset's command string
and to retrieve the connection that was passed to it, if there is
one. Finally, RowSetInternal
methods allow a reader to
set a new RowSetMetaData
object, which describes to
the rowset the rows that the reader will insert into it. The name
of the interface is RowSetInternal
for good reason.
Its methods are used internally; an application does not call these
methods directly.
Rowsets may be either connected
or disconnected. A connected RowSet
object
maintains a connection to its data source the entire time it is in
use, whereas a disconnected rowset is connected to its data source
only while it is reading data from the data source or writing data
to it. While the rowset is disconnected, it does not need a JDBC
driver or the full implementation of the JDBC API. This makes it
very lean and therefore an ideal container for sending a set of
data to a thin client. The client can, if it chooses, make updates
to the data and send the rowset back to the application server. On
the server, the disconnected RowSet
object uses its
reader to make a connection to the data source and write data back
to it. Exactly how this is done depends on how the reader is
implemented. Typically, the reader delegates making a connection
and reading data to the JDBC driver.
RowSet
event model makes it
possible for a Java object, or component, to be notified about
events generated by a RowSet
object. Setting up the
notification mechanism involves both the component to be notified
and the RowSet
object itself. First, each component
that wants to be notified of events must implement the
RowSetListener
interface. Then the RowSet
object must register each component by adding it to its list of
components that are to be notified of events. At this point, such a
component is a listener, an instance of a class that implements the
RowSetListener
methods and is registered with a
RowSet
object.
Three kinds of events can occur in a
RowSet
object: its cursor can move, one of its rows
can change (be inserted, deleted, or updated), or its entire
contents can be changed. The RowSetListener
methods
cursorMoved
, rowChanged
, and
rowSetChanged
correspond to these events. When an
event occurs, the rowset will create a RowSetEvent
object that identifies itself as the source of the event. The
appropriate RowSetListener
method will be invoked on
each listener, with the RowSetEvent
object being
passed to the method. This will inform all of the rowset's
listeners about the event.
For example, if a pie chart component,
pieChart
, wants to display the data in the
RowSet
object rset
, pieChart
must implement the RowSetListener
methods
cursorMoved
, rowChanged
, and
rowSetChanged
. The implementations of these methods
specify what pieChart
will do in response to an event
on rset
. After implementing these methods,
pieChart
can be registered with rset
.
When pieChart
is added as a listener to
rset
, it will be notified when an event occurs on
rset
by having the appropriate method invoked with a
RowSetEvent
object as its parameter. The listener
pieChart
can then update itself to reflect the current
data and cursor position of rset
. If
pieChart
does not need to reflect one of the events on
rset
, it can implement the RowSetListener
method for that event so that it does nothing. For instance, if
pieChart
does not need to show the current cursor
position in rset
, it can have the method
cursorMoved
do nothing.
Any number of components may be listeners
for a given RowSet
object. If, for example, the bar
graph component barGraph
is also displaying the data
in rset
, it can become a listener by implementing the
RowSetListener
methods and then being registered with
rset
. The following lines of code register the two
components pieChart
and barGraph
as
listeners with rset
.
rset.addRowSetListener(pieChart); rset.addRowSetListener(barGraph);Removing a listener is done in a similar fashion with the method
RowSet.removeListener
.
rset.removeRowSetListener(pieChart); rset.removeRowSetListener(barGraph);The code for setting up the listeners for a rowset is often generated by a tool, which means that an applications programmer only needs to specify a rowset and the components that are to be notified when an event occurs on that rowset. After the listeners are set up, the processing of an event is done largely behind the scenes. For example, if an application updates a row in
rset
, rset
will
internally create a RowSetEvent
object and pass it to
the rowChanged
methods implemented by
pieChart
and barGraph
. The listeners will
know where the event occurred because the RowSetEvent
object passed to rowChanged
is initialized with
rset
, the RowSet
object that is the
source of the event. The components pieChart
and
barGraph
will update their displays of the row
according to their own implementations of the
RowSetListener.rowChanged
method.
In the following code fragment,
pieChart
and barGraph
are registered as
listeners with the RowSet
object rset
.
After rset
fills itself with new data by calling the
method execute
, event notification takes place behind
the scenes. (The method execute
will be explained in
detail in the section "Executing
a Command ," on page 805.) As the first step in the event
notification process, the RowSetEvent
object
rsetEvent
is created and initialized with
rset
. Next the pieChart
and
barGraph
versions of the method
rowSetChanged
are called with rsetEvent
as their arguments. This tells pieChart
and
barGraph
that all the data in rset
has
changed, and each listener will carry out its own implementation of
the method rowSetChanged
.
rset.addRowSetListener(pieChart); rset.addRowSetListener(barGraph); . . . rset.execute(); // The following methods will be invoked behind the scenes: RowSetEvent rsetEvent = new RowSetEvent(this); pieChart.rowSetChanged(rsetEvent); barGraph.rowSetChanged(rsetEvent);
RowSet
interface provides a
set of JavaBeans properties so that a RowSet
instance
can be configured to connect to a data source and retrieve a set of
rows. Some properties may not be required, depending on particular
implementations. For example, either a URL or a data source name is
required for establishing a connection, so if one property is set,
the other one is optional. If both are set, the one set more
recently is used. If data for a rowset is being retrieved from a
non-SQL data source that does not support commands, such as a
spreadsheet, the command property does not need to be set. Setting
some properties is optional if the default is already the desired
property. For example, escape processing is on by default, so an
application does not need to set escape processing unless it wants
to disable it. (Escape processing is explained in "SQL Escape Syntax in Statements," on
page 958.)
The following list gives the getter and
setter methods defined on the RowSet
interface for
retrieving and setting a RowSet
object's properties.
The two exceptions are the methods getConcurrency
and
getType
, which are inherited from the
ResultSet
interface rather than being defined in the
RowSet
interface.
getCommand |
setCommand |
---|---|
ResultSet.getConcurrency |
setConcurrency |
getDataSourceName |
setDataSourceName |
getEscapeProcessing |
setEscapeProcessing |
getMaxFieldSize |
setMaxFieldSize |
getMaxRows |
setMaxRows |
getPassword |
setPassword |
getQueryTimeout |
setQueryTimeout |
getTransactionIsolation |
setTransactionIsolation |
ResultSet.getType |
setType |
getTypeMap |
setTypeMap |
getUrl |
setUrl |
getUsername |
setUsername |
In addition, the standard JDBC RowSet
implementations may have various properties that are specific to
them.
The following code fragment, in which
rset
is a RowSet
object, sets properties
that are typically required for establishing a connection with a
data source using a DataSource
object.
rset.setDataSourceName("jdbc/logicalDataSourceName"); rset.setUsername("cervantes"); rset.setPassword("secret");Note that
jdbc/logicalDataSourceName
is the name that has been
registered with a JNDI (Java Naming and Directory Interface) naming
service. When an application gives the naming service the logical
name, it will return the DataSource
object that has
been bound to the logical name. "Using JNDI," on page 568,
explains using DataSource
objects and the JNDI API.
Using a DataSource
object instead of hardcoding
connection information makes code more portable and makes
maintaining it much easier. If the host machine or port number of a
data source changes, for example, only the properties of the
DataSource
object entered in the JNDI naming service
need to be updated, not every application that gets a connection to
that data source.
A RowSet
object also has
methods for setting properties that affect command execution. For
example, as a sampling of these methods, the following code
fragment sets twenty seconds as the longest a driver will wait for
a statement to execute, sets 1024 as the largest number of rows
rset
may contain, and specifies that rset
will be allowed to read only data from committed transactions.
rset.setQueryTimeout(20); rset.setMaxRows(1024); rset.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);The type and concurrency may also be set for a
RowSet
object, as shown in the following lines of
code.
rset.setType(ResultSet.TYPE_SCROLL_INSENSITIVE); rset.setConcurrency(ResultSet.CONCUR_UPDATABLE);The first line sets
rset
to be
scrollable but not sensitive to the updates made while it is open.
A RowSet
object that maintains a continuously open
connection with a data source may be
TYPE_SCROLL_SENSITIVE
, but one that does not is
incapable of being sensitive to changes made by other objects or
transactions. The second line of code sets rset
to be
updatable, meaning that it can modify its data.
Note that a RowSet
object
may be scrollable even if it uses a driver that does not support
scrollable result sets. In fact, a rowset may often be used in
place of a regular result set as a way of getting a scrollable
result set. This is discussed in more detail in the section
"Traversing a RowSet
Object," on page 804.
An application fills a
RowSet
instance with data by executing the
RowSet
object's command string. This string must be a
query that, when executed, will produce a result set from which the
RowSet
object will get its data. The method
setCommand
sets the String
object
supplied to it as the command that will be executed when the method
execute
is invoked. For example, the following line of
code sets the command string to a query that selects the name and
salary from every row in the table EMPLOYEES
.
rset.setCommand("SELECT NAME, SALARY FROM EMPLOYEES");
With the preceding command string set, after
rset
invokes its execute
method, it will
contain exactly the same data as the result set that the query
produces (one row for each row in the table EMPLOYEES
,
with each row containing a name and a salary).
PreparedSatement
object. Note that the
parameters must be input parameters and not output parameters.
A newly created RowSet
object exists but has no data until it is populated with a call to
the method execute
or populate
. In order
to use the method execute
, the necessary properties
must be set, and values for any placeholder parameters must be set.
Parameter values can be set at run time, which, for example, allows
an application to be interactive and accept user input.
The RowSet
interface, like
the PreparedStatement
interface, has setter methods
for setting the value of input parameters. There is a setter method
for each data type, including the SQL99 data types. The following
code fragment sets the command string and then sets its two input
parameters with values. Assuming that the column DEPT
stores values of type VARCHAR
, the method
setString
is used to set both parameters because that
is the appropriate method for setting VARCHAR
values.
rset.setCommand(
"SELECT NAME, SALARY FROM EMPLOYEES WHERE DEPT = ? OR DEPT = ?");
rset.setString(1, "SALES");
rset.setString(2, "MARKETING");
After this command is executed,
rset
will contain the names and salaries of the
employees in the sales and marketing departments.
Any parameters in a RowSet
object's command string must be set with values before the command
is executed. When a rowset is disconnected, the parameters that
have been set are used by the reader's readData
method, which is invoked internally by the method
execute
. A rowset stores these parameter values in an
internal hashtable, and the readData
method retrieves
them with a call to the rowset's
RowSetInternal.getParams
method.
javax.sql.RowSet
interface
extends the java.sql.ResultSet
interface, so moving
the cursor in a scrollable RowSet
object is exactly
the same as moving a cursor in a scrollable ResultSet
object. A RowSet
object inherits all of the
ResultSet
methods, so it is really a result set with
added features that allow it to function as a JavaBeans component.
Most components that use instances of RowSet
are
likely to treat them as ResultSet
objects.
Even though the methods for moving a
RowSet
object's cursor are identical to those of a
ResultSet
object from the user's point of view, a
RowSet
object's implementation of these methods is
different. A RowSet
object needs to let the listeners
registered with it know about each movement of its cursor.
Consequently, the cursor movement methods for a RowSet
object are implemented to trigger the internal event notification
process. For example, when the method next
is called,
its implementation will create a RowSetEvent
object
and call each listener's cursorMoved
method, supplying
the RowSetEvent
object as the parameter. The listener
uses the RowSetEvent
object to find out in which
RowSet
object the cursor has moved and then invokes
its implementation of the method cursorMoved
. The
implementation could do nothing, or it could, for example, call the
method ResultSet.getRow
to get the cursor's current
position and update the listener's display of that row's data.
To demonstrate that cursor movements are
the same in RowSet
and ResultSet
, the
following code fragment iterates forward through the
RowSet
object rset
and prints out the two
values retrieved from each row.
rset.beforeFirst(); while (rset.next()) { System.out.println(rset.getString(1) + " " + rset.getFloat(2)); }Other cursor movements are also identical to those in the
ResultSet
interface. .
RowSet
interface provides
the method execute
, which is invoked to fill a
RowSet
object with data. There can be many variations
in the implementation of this method, and subtypes may define
additional methods for populating themselves with data. The
execute
method makes use of rowset properties and will
throw an SQLException
if the necessary properties have
not been set. Standard properties have been defined; however,
additional properties depend on each particular RowSet
implementation, so application writers should check the
documentation for the implementation they are using.
A disconnected rowset needs a reader (an
object that implements the RowSetReader
interface) and
a writer (an object that implements the RowSetWriter
interface) . In the JDBC RowSet
Implementations, a
reader and writer are encapsulated in the the
SyncProvider
class, which is part of the
javax.sql.rowset.spi
package. This package, known as
the SPI (Service Provider Interface), is explained later. The SPI
makes it much easier to implement and deploy a reader and writer.
The two reference implementations,
RIOptimisticProvider
and RIXmlProvider
,
provide immediate implementations.
A disconnected rowset must also implement
the RowSetInternal
interface to make additional access
to its internal state available to the reader and writer. With its
reader/writer framework in place, a rowset's execute
method is able to delegate tasks to its reader and writer
components.
With the addition of the standard
RowSet
implementations, developers will find it much
easier to implement the reader/writer facilities. They can leverage
the reader/writer facilities that are already included in these
standard implementations by simply incorporating them into their
own implementations.
In a typical implementation, a
disconnected rowset's execute
method will invoke the
reader's readData
method to accomplish the job of
reading new data into the rowset. Generally, after clearing the
rowset of its current contents, the readData
method
will get the properties it needs and establish a connection with
the data source. If there are any parameters to be set,
readData
retrieves them from the rowset and sets them
appropriately in the rowset's command string. Then the
readData
method executes the command string to produce
a result set. Finally, readData
populates the rowset
with the data from the result set and sets that data as the
original values.
The reader's readData
method
may also be implemented to set the rowset's metadata. One of the
many possible implementations is to have the readData
method create a RowSetMetaData
object and set it with
information about the columns in the data source that is about to
be read. The readData
method next sets the new
RowSetMetaData
object to be the one associated with
the rowset. The rowset can then use the RowSetMetaData
object to see the format for the data that will be read into
it.
When the rowset's command string is
executed, all of a rowset's listeners need to be notified so that
they can take the appropriate action. The execute
method will invoke the rowSetChanged
method on each
listener, supplying rowSetChanged
with a newly created
RowSetEvent
object that identifies the
RowSet
object in which the event occurred. As is true
of most of what the method execute
does, the
notification of listeners is invisible to the application
programmer using a rowset.
One more task for the
execute
method is that of setting the "original"
values maintained by the rowset. These are the values, returned by
the method RowSetInternal.getOriginal
, that existed
immediately before the most recent update. A RowSet
object's original values may be the values it got from the data
source, but this is not necessarily true. The first time a
RowSet
object's data is synchronized with its data
source, its original values will be the same as the values it
originally got from the data source. However, if some of a
RowSet
object's values are modified a second time, the
original values will be the values that existed after the first
modifications, not those from the data source. The point of keeping
track of a RowSet
object's original values is to be
able to check whether the corresponding values in the data source
have been changed.
If a SyncProvider
object is
implemented to check for conflicts, it compares the original values
with the ones in the data source. If the rowset's original values
(pre-modification values) and the underlying data source's values
are not the same, there will be a conflict if the
RowSet
object's modified values are written to the
data source. If the values are the same, however, there is no
conflict. Depending on the synchronization model being used, a
SyncProvider
object may or may not write the new
rowset values to the data source when there is a conflict.
If the SyncProvider
implementation does not write new values to the underlying data
source, the execute
method will reset the original
values back to the values currently in the rowset. Then the next
time execute
is called, which changes all of the
values in the rowset, the SyncProvider
object can
retrieve these values to compare with those in the underlying data
source to see if there is a conflict.
In summary, when an application invokes
the execute
method, many operations take place behind
the scenes. When the rowset is disconnected, the following take
place: the contents of the rowset are replaced with new data, any
listeners using the rowset's data are notified, the rowset's
metadata is updated, and the rowset's original values are set to
the current data values. For a connected rowset, the
execute
method generally just populates the rowset
with new data and notifies the listeners of the event. There is no
possibility of a conflict because changes made to a connected
RowSet
object are also made to the data source.
RowSet
object maintains a set
of metadata about the columns it contains. Being derived from
ResultSet
, a RowSet
object's metadata can
be retrieved with ResultSetMetaData
methods in the
same way that a ResultSet
object's metadata can. For
instance, the following code fragment creates a
RowSetMetaData
object for the RowSet
object rset
and finds out how many columns
rset
contains. Note that the method
getMetaData
is a ResultSet
method that
returns a ResultSetMetaData
object, so it must be cast
to a RowSetMetaData
object before it can be assigned
to rsetmd
.
RowSetMetaData rsetmd = (RowSetMetaData)rset.getMetaData(); int columnCount = rsetmd.getColumnCount();The variable
rsetmd
contains
information about the columns in the RowSet
rset
. Any of the RowSetMetaData
methods
can be invoked on rsetmd
to retrieve the information
that rsetmd
contains.
The interface RowSetMetaData
defines setter methods corresponding to each of the getter methods
defined in ResultSetMetaData
(except that there are no
methods for setting the class name for a column or for setting
whether the column is read-only, possibly writable, or definitely
writable). The RowSetMetaData
setter methods are
called by a reader after it has read new data into a rowset and
created a new RowSetMetaData
object for describing the
RowSet
object's columns. The following code shows what
a reader might do behind the scenes. It creates the new
RowSetMetaData
object rowsetmd
for the
RowSet
object rowset
, sets the
information for the columns, and finally calls the
RowSetInternal
method setMetaData
to set
rowsetmd
as the metadata for rowset
.
rowset.execute(); // ... as part of its implementation, execute calls readData reader.readData((RowSetInternal)this); // ... as part of the implementation of readData, the reader for // the rowset would do something like the following to update the // metadata for the rowset RowSetMetaData rowsetmd = new ...; // create an instance of a class // that implements RowSetMetaData rowsetmd.setColumnCount(3); rowsetmd.setColumnType(1, Types.INTEGER); rowsetmd.setColumnType(2, Types.VARCHAR); rowsetmd.setColumnType(3, Types.BLOB); // ... set other column information rowset.setMetaData(rowsetmd);
RowSet
interface with the
release of J2SE 5.0. These implementations are being provided as an
aid for those who want to write their own implementations. The
RowSet
interface may be implemented in any number of
ways to serve any number of different purposes, and anyone may
implement it. The expectation is, however, that RowSet
implementations will be written mostly by driver vendors, who may
include their implementations as part of their JDBC products.
Implementors are free to use the reference implementations just as
they are, to build on them, or to write their implementations
completely on their own.
The standard implementations consist of
two parts, the interfaces and the reference implementations. The
interfaces are in the javax.sql.rowset
package; the
implementations are in the com.sun.rowset
package.
They were developed with input from experts in the database field
through the Java Community Process as JSR 114. The goal was to
standardize key rowset functionality so that developers can
leverage it in their own implementations. The standard
RowSet
implementations are:
RowSet
implementations extend the abstract class BaseRowSet
and implement the appropriate interface (JdbcRowSet
,
CachedRowSet
, WebRowSet
,
FilteredRowSet
, or JoinRowSet
). Note that
the BaseRowSet
class provides a base implementation of
the common functionality for all RowSet
objects, which
developers may choose to use or not. The BaseRowSet
class includes the following:
RowSet
object's command
propertyRowSet
implementations have
the following default values:RowSet
object's commandBINARY
, VARBINARY
,
LONGVARBINARY
, CHAR
,
VARCHAR
, or LONGVARCHAR
may containnull
Hashtable
object for storing the
values set for the placeholder parameters in the
RowSet
object's command As stated previously, RowSet
objects may be connected or disconnected. The RowSet
implementations fall into the following categories:
JdbcRowSet
The CachedRowSet
interface
provides the methods that a disconnected RowSet
object
needs. In the standard implementations, a disconnected
RowSet
extends the BaseRowSet
class and
implements the CachedRowSet
interface. In addition,
the JoinRowSet
implementation implements the
JoinRowSet
interface, the FilteredRowSet
implementation implements the FilteredRowSet
interface, and the WebRowSet
implementation implements
the WebRowSet
interface.
JdbcRowSet
object (an
instance of the standard implementation of the
JdbcRowSet
interface) is, like all rowsets, a
container for a set of rows. The source of these rows is always a
ResultSet
object because a JdbcRowSet
object is a connected RowSet
object. In other words,
it always maintains a connection with a DBMS via a JDBC driver.
Note that other implementations may use any tabular data, such as a
flat file or a spreadsheet, as their source of data if their reader
and writer facilities are appropriately implemented.
A JdbcRowSet
object has many
uses. Probably the most common use is to make a
ResultSet
object scrollable and thereby make better
use of legacy drivers that do not support scrolling. A
JdbcRowSet
object's rows of data (and those in any
RowSet
object) are identical to those in the
ResultSet
object that is the result of executing the
rowset's command. Therefore, if the rowset is scrollable, it is the
equivalent of having a scrollable ResultSet
object
even if the ResultSet
object itself is not
scrollable.
Another common use is to make the driver
or a ResultSet
object a JavaBeans component. Like all
RowSet
objects, a JdbcRowSet
object is a
JavaBeans component. By being continuously connected to a driver,
it serves as a wrapper for the driver, which effectively makes the
driver a JavaBeans component. This means that a driver presented as
a JdbcRowSet
object can be one of the Beans that a
tool makes available for composing an application. Being
continuously connected also means that a JdbcRowSet
object is able to serve as a wrapper for its ResultSet
object. It can take calls invoked on it and, in turn, call them on
its ResultSet
object. As a consequence, the
ResultSet
object can be, for example, a component in a
GUI application that uses Swing
technology.
The following code fragment illustrates
creating a JdbcRowSet
object, setting its properties,
and executing the command string in its command
property. The JdbcRowSet
implementation provides a
default constructor, but being a JavaBeans component, a
JdbcRowSet
implementation will probably most often be
created by a visual JavaBeans development tool.
JdbcRowSet jrs = new JdbcRowSetImpl(); jrs.setCommand("SELECT * FROM TITLES); jrs.setURL("jdbc:myDriver:myAttribute"); jrs.setUsername("cervantes"); jrs.setPassword("sancho"); jrs.execute();At this point,
jrs
contains all
of the data in the table TITLES
because the
ResultSet
object generated by jrs
's
command contains all of the data in the table TITLES
.
From this point on, the code can simply
use ResultSet
methods because it is effectively
operating on a ResultSet
object. It can navigate the
rows in jrs
, retrieve column values, update column
values, insert new rows, and so on. For example, the next two lines
of code go to the second row and retrieve the value in the first
column using ResultSet
methods.
jrs.absolute(2); String title = jrs.getString(1);
CachedRowSet
interface (CachedRowSetImpl
in the package com.sun.rowset
) provides a container
for a set of rows that is being cached in memory outside of a data
source. It is disconnected, serializable, updatable, and
scrollable. Because a CachedRowSet
object caches its
own data, it does not need to maintain an open connection with a
data source and is disconnected from its data source except when it
is reading or writing data. But because it stores its rows in
memory, a CachedRowSet
object is not appropriate for
storing extremely large data sets. However, to accommodate larger
amounts of data, a CachedRowSet
object may page in
data, reading only a specified number of rows at a time.
A CachedRowSet
object can
populate itself with data from a tabular data source, and because
it is updatable, it can also modify its data. As with all
RowSet
objects, in addition to getting data in, it can
get data out, propagating its modifications back to the underlying
data source.
CachedRowSet
object is especially well suited for use
with a thin client. Not being continually connected to its data
source, it does not require the presence of a JDBC driver and can
therefore be much more lightweight than a ResultSet
object or a connected RowSet
object.
One of the major uses for a
CachedRowSet
instance is to pass tabular data between
different components of a distributed application, such as
EnterpriseJavaBeans TM
(EJBTM) components running in an
application server. The server can use the JDBC API to retrieve a
set of rows from a database and then use a
CachedRowSet
object to send the data over the network
to, for example, a thin client running in a web browser.
Another use for a
CachedRowSet
object is to provide scrolling and
updating capabilities to a ResultSet
object that does
not itself have this functionality. For example, as with a
JdbcRowSet
object, an application can create a
CachedRowSet
object initialized with the data from a
ResultSet
object and then operate on the rowset
instead of the result set. With the rowset set to be scrollable and
updatable, the application can move the cursor, make updates, and
then propagate the updates back to the data source.
The following code fragment populates the
CachedRowSet
object crset
with the data
from the ResultSet
object rs
.
ResultSet rs = stmt.executeQuery("SELECT * FROM AUTHORS"); CachedRowSet crset = new CachedRowSetImpl(); crset.populate(rs);Once the rowset is populated, an application can pass it across the network to be manipulated by distributed components, or it can operate on
crset
instead of
rs
to gain scrollability or updatability.
The CachedRowSet
implementation uses the base API from the BaseRowSet
abstract class and the CachedRowSet
interface, which
adds methods for the additional functionality it needs. For
example, it needs a way to connect to a data source and read data
from it. If a CachedRowSet
object's data is modified
while it is disconnected, it also needs a way to connect to the
data source and write the new data back to it. The reader and
writer facilities, based on the RowSetReader
and
RowSetWriter
interfaces and encapsulated in a
SyncProvider
implementation, provide these
capabilities.
A writer implementation supplies a
mechanism for updating the data source with any changes it has made
while it was disconnected, thus making the modified data
persistent. This can get complicated if there is a
conflict, that is, if another user has already modified
the same data in the data source. The SyncProvider
class includes mechanisms that afford varying degrees of control
over how accessible data in the database is to others, and
consequently, it has some control over the number of conflicts that
may occur. Even more important, a
SyncProvider
implementation determines the level of
care to be taken in synchronization.
The JDBC RowSet
Implementations specification includes two different reference
implementations for synchronization using two different levels of
concurrency. The RIXmlProvider
does not check for
conflicts and simply writes its modified data to the data source.
The writer defined in the RIOptimisticProvider
implementation checks for conflicts, and if a conflict exists,
allows an application to use a SyncResolver
object to
decide whether or not to write modified data on a case by case
basis. Third parties can write SyncProvider
implementations offering different degrees of synchronization, and
a disconnected rowset may use the one that best fits its needs. The
SyncProvider
class and the SyncResolver
interface are covered in the section "Using the
javax.sql.rowset.spi
Package".
The implementation of the
CachedRowSet
interface can be used as a basis for
implementing other disconnected rowsets. For example, the
implementations of the WebRowSet
,
FilteredRowSet
, and JoinRowSet
interfaces
are all based on the implementation of the
CachedRowSet
interface. Therefore, the following
discussion of the CachedRowSet
interface goes into
some detail about the methods it defines and what they do. The
sections on the other disconnected rowset implementations discuss
the capabilities they have beyond those of the
CachedRowSet
implementation.
RowSet
object is a
JavaBeans component, so developers will often create them using a
visual JavaBeans development tool while they are assembling an
application. It is also possible for an application to create an
instance at run time, using a public constructor provided by a
class that implements the RowSet
interface. For
example, the standard implementation of the
CachedRowSet
interface defines a public default
constructor, so an instance of CachedRowSet
can be
created with the following line of code.
CachedRowSet crset = new CachedRowSetImpl();The newly created
crset
is a
container for a set of data, and it will have the default
properties of a BaseRowSet
object. Because its
implementation uses the BaseRowSet
class,
crset
can use BaseRowSet
methods to set
new values for any of these properties or to set values for other
properties as appropriate. For example, in the following code
fragment, the first line sets the command string that
crset
will use to get its data, and the second line
turns escape processing off.
crset.setCommand("SELECT * FROM EMPLOYEES"); crset.setEscapeProcessing(false);
CachedRowSet
object must
obtain a SyncProvider
object, which implements a
reader and a writer, in order to get data from a data source and to
write its modifications of data back to the data source. The
SyncFactory
creates an instance of a
SyncProvider
object that is registered with it when a
RowSet
object requests it. Because the constructor
that created crset
was not given any parameters, the
SyncFactory
provided an instance of the default, which
is RIOptimisticProvider
(one of the two
implementations of the SyncProvider
class provided
with the standard RowSet
implementations).
If the fully qualified class name of a
SyncProvider
implementation is supplied to the
constructor, the SyncFactory
will initialize the
RowSet
object with an instance of that
SyncProvider
implementation if it has been registered
with the SyncFactory
. The following line of code
creates a CachedRowSet
object whose synchronization
provider is an instance of the given SyncProvider
implementation.
CachedRowSet crset = new CachedRowSetImpl("com.supersoftware.providers.HighAvailabilityProvider");After its creation, a
RowSet
object can be set with another registered SyncProvider
implementation, including third-party implementations or the other
standard provider implementation, RIXmlProvider,
which
reads and writes a RowSet
object in XML. The way to
set a SyncProvider
object is to use the
CachedRowSet
method setSyncProvider
,
which resets the current SyncProvider
object to the
one specified. For example, if the current provider for
crset
is an RIOptimisticProvider
object,
the following line of code changes the provider to a
com.supersoftware.providers.HighAvailabilityProvider
object.
crset.setSyncProvider("com.supersoftware.providers.HighAvailabilityProvider");
CachedRowSet
object
can contain data that was retrieved from a data source such as a
file or spreadsheet, it will probably get its data primarily from a
ResultSet
object via a JDBC driver. The
RowSet
interface provides the method
execute
, which takes no parameters, and the
CachedRowSet
interface adds a second version of the
method execute
that takes a Connection
object as a parameter. Both methods populate a
CachedRowSet
object with data from the
ResultSet
object that is created when the
CachedRowSet
object's command is executed. The
CachedRowSet
interface also adds the method
populate
, which takes a ResultSet
object
as a parameter.
The version of execute
inherited from the RowSet
interface has to establish a
connection behind the scenes before it can execute the rowset's
command string. The version of execute
defined in the
CachedRowSet
interface is passed a
Connection
object, so it does not need to establish
its own connection. The method populate
does not need
to establish its own connection or execute a query because it is
passed the ResultSet
object from which it will get its
data. Thus, the method populate
does not require that
the properties affecting a connection be set before it is called,
nor does it require that the command string be set. The following
code fragment shows how the method populate
might be
used.
ResultSet rs = stmt.executeQuery("SELECT NAME, SALARY FROM EMPLOYEES"); // The code that generates rs . . . CachedRowSet crset = new CachedRowSetImpl(); crset.populate(rs);The new
CachedRowSet
object
crset
contains the same data that the
ResultSet
object rs
contains.
The implementation details of the method
populate
may vary, of course, but because it is passed
a ResultSet
object, populate
is in some
ways quite different from the method execute
. As
stated previously, the populate
method does not need
to establish a connection. Nor does it use a reader because it can
get its data directly from the given result set. By using
ResultSetMetaData
methods, it can get information
about the format of the result set so that it is able to use the
appropriate getter methods in the ResultSet
interface
to retrieve the ResultSet
object's data.
The methods execute
and
populate
do have some similarities. The main
similarity is that both methods change the contents of the entire
rowset. As a consequence, they both cause the following:
notification of listeners, setting the original values equal to the
current rowset values, and updating the rowset's metadata
information.
The following code fragments give
examples of using the two versions of the method
execute
. Both versions execute a query, so both
require that a command string be set. When no connection has been
passed to the method execute
, the appropriate
connection properties must be set before the command can be called.
The execute
method will call the rowset's reader,
which will use the necessary properties to establish a connection
with the data source. Once the connection is established, the
reader can call the method executeQuery
to execute the
rowset's command string and get a result set from which to retrieve
data.
CachedRowSet crset1 = new CachedRowSetImpl(); crset1.setCommand("SELECT NAME, SALARY FROM EMPLOYEES"); crset1.setDataSourceName("jdbc/myDataSource"); crset1.setUsername("paz"); crset1.setPassword("p38c3"); crset1.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); crset1.execute();In the next example, the
Connection
object con
is passed to the
method execute
. The CachedRowSet
object
crset2
will use con
for executing the
command string instead of having to establish a new connection
behind the scenes.
CachedRowSet crset2 = new CachedRowSetImpl(); crset2.setCommand("SELECT NAME, SALARY FROM EMPLOYEES"); crset2.execute(con);Both
crset1
and
crset2
were connected to the data source while the
command string was executed and while the resulting data was read
and inserted into it. After the method execute
returns, both will close their connections to their data sources.
CachedRowSet
object, like all
rowsets, uses the getter methods inherited from the
ResultSet
interface to access its data. Because a
CachedRowSet
object is implemented so that it is
scrollable by default, it can also use the ResultSet
methods for moving the cursor. For example, the following code
fragment moves the cursor to the last row of the
CachedRowSet
object crset
and then
retrieves the String
value in the first column of that
row.
crset.last(); String note = crset.getString(1);A
CachedRowSet
object is
implemented such that it always has type
ResultSet.TYPE_SCROLL_INSENSITIVE
, so in addition to
being scrollable, a CachedRowSet
object is always
insensitive to changes made by others. This makes sense because a
CachedRowSet
object is mostly disconnected, and while
it is disconnected, it has no way of seeing the changes that others
might make to the underlying data source from which it got its
data.
CachedRowSet
object can have its entire contents
changed with its execute
and populate
methods. It can have one row at a time modified with the
ResultSet
updater methods and the methods
insertRow
and deleteRow
. Note that the
JDBC RowSet
Implementations specification does not say
where an inserted row should go. In the reference implementations,
inserted rows are put immediately after the current row, but there
is great flexibility for deciding where rows are inserted.
An updater method modifies the value in the specified column in the current row, assigning it the value passed to it (usually as the second parameter). An updater method changes only the value in the rowset, which is cached in memory; it does not affect the value in the underlying data source. Also, it does not affect the value that the rowset keeps track of as the original value.
When an application has made all its
updates to a row, it calls the method updateRow
. In a
CachedRowSet
implementation, this method signals that
the updates for the current row are complete, but, like the updater
methods, it does not affect the values in the underlying data
source. The updateRow
method likewise does not affect
the values stored as original values. After calling the
updateRow
method on all the rows being updated, an
application needs to call the CachedRowSet
method
acceptChanges
. This method invokes a writer component
internally to propagate changes to the data source backing the
rowset, and for each column value that was changed, it sets the
original value to the current value.
Once it has called
updateRow
, an application may no longer call the
method cancelRowUpdates
to undo updates to the current
row. If it has not yet invoked the method
acceptChanges
, however, it can call the method
restoreOriginal
, which undoes the updates to all rows
by replacing the updated values in the rowset with the original
values. The restoreOriginal
method does not need to
interact with the underlying data source.
The following code fragment updates the
first two rows in the CachedRowSet
object
crset
.
crset.execute(); // crset is initialized with its original and current values crset.first(); crset.updateString(1, "Jane_Austen"); crset.updateFloat(2, 150000f); crset.updateRow(); // the current value of the first row has been updated crset.relative(1); crset.updateString(1, "Toni_Morrison"); crset.updateFloat(2, 120000f); crset.updateRow(); // the current value of the second row has been updated crset.acceptChanges(); // the original value has been set to the current value and the // database has been updated
RowSet
implementations can take advantage of the ability to customize the
retrieval and updating of data that the rowset framework provides.
This applies to rowsets that are disconnected, such as
CachedRowSet
objects, because they require the
services of a reader and a writer, which are encapsulated by a
SyncProvider
object.
The CachedRowSet
interface
defines the method setSyncProvider
, which provides a
CachedRowSet
object with its implementation of a
reader and writer. It also defines a getSyncProvider
method for retrieving a rowset's SyncProvider
object.
A rowset's reader and writer operate completely behind the scenes,
performing any number of tasks that can be customized to provide
additional functionality.
The RowSetReader
interface
has one public method, readData
, which can be
customized in various ways. For example, a reader can be
implemented so that it reads data straight from a file or from some
other non-SQL data source rather than from a database using a JDBC
driver. Such a reader might use the method
RowSet.insertRow
to insert new rows into the rowset.
When invoked by a reader, this method could also be implemented so
that it updates the original values stored by the rowset.
The RowSetWriter
interface
has one public method, writeData
, which writes data
that has been modified back to the underlying data source. This
method can likewise be customized in a variety of ways. The writer
establishes a connection with the data source, just as the reader
does, and depending on how it is implemented, may or may not check
for conflicts. The RowSetInternal
methods
getOriginal
and getOriginalRow
supply the
values that existed before the current modifications, so the writer
can compare them with the values read from the data source to see
if the data source has been modified. How the writer decides
whether or not to write data when there is a conflict again depends
on how it is implemented.
A CachedRowSet
object that
has been configured with a custom reader and/or writer can be made
available as a normal JavaBeans component. This means that
developers writing applications do not have to worry about
customizing readers and writers and can concentrate on the more
important aspects of using rowsets effectively.
CachedRowSet
interface
defines a number of other methods. For example, it defines two
versions of the method toCollection
. Sometimes it is
more convenient to work with a rowset's data as elements in a
collection, which these methods make possible by converting a
rowset's data into a Java collection. Other
CachedRowSet
methods create a copy of the rowset.
CachedRowSet.clone
and
CachedRowSet.createCopy
create an exact copy of the
rowset that is independent from the original. By contrast, the
CachedRowSet.createShared
method creates a rowset that
shares its state with the original rowset. In other words, both the
new and the original rowset share the same physical, in-memory copy
of their original and current values. If an updater method is
called on one shared rowset, the update affects the other rowset as
well. In effect, the createShared
method creates
multiple cursors over a single set of rows.
javax.sql.rowset.spi
provides the API that a developer
needs to use to implement a synchronization provider. It includes
the following classes and interfaces:
SyncFactory
SyncFactoryException
SyncProvider
SyncProviderException
SyncResolver
XmlReader
XmlWriter
TransactionalWriter
javax.sql
:
A CachedRowSet
object or other
disconnected RowSet
object gets its
SyncProvider
object from a SyncFactory
object. Being a static class, there is only one instance of
SyncFactory
, which means that there is only one source
from which a disconnected RowSet
object can obtain its
SyncProvider
object. A vendor can register its
implementation of the SyncProvider
abstract class with
the SyncFactory
, which then makes it available for a
RowSet
object to plug in. The following line of code
shows one way to register a SyncProvider
implementation with the SyncFactory
.
SyncFactory.registerProvider("com.supersoftware.providers.HAProvider");Note that the argument supplied to
registerProvider
is the fully qualified class name of
the SyncProvider
implementation.
A SyncProvider
implementation may also be registered by adding it to the system
properties. This can be done at the command line at execution time
as follows:
-Drowset.provider.classname=com.supersoftware.providers.HAProviderAnother way to register a
SyncProvider
implementation is to add the fully
qualified class name, the vendor, and the version number to the
standard properties file. The reference implementation comes with a
properties file that already has entries for the two reference
implementation synchronization providers,
RIOptimisticProvider
and RIXmlProvider
.
The fourth way to register a SyncProvider
implementation is to register it on a JNDI context and then
register the JNDI context with the SyncFactory
by
supplying the fully qualified provider name to the method
SyncFactory.registerJNDIContext
.
When a RowSet
object
requests a particular SyncProvider
implementation, the
SyncFactory
will search for it and create an instance
of it and return it to the RowSet
object. For example,
in the following line of code, the CachedRowSet
object
crset
requests the
com.supersoftware.providers.HAProvide
r.
crset.setSyncProvider("com.supersoftware.providers.HAProvider");If the requested provider has not been registered in any of the possible ways, the
SyncFactory
throws a SyncFactoryException
object.
A CachedRowSet
object or any
of its subclasses can discover which SyncProvider
implementations have been registered with the
SyncFactory
and are thus available for its use by
calling the following line of code.
java.util.Enumeration providers = SyncFactory.getRegisteredProviders();The core components of a
SyncProvider
object are the reader and writer that it
implements. The implementation of the reader determines from what
kind of data source (relational database, flat file, spreadsheet,
and so on) it can read data and also how it reads that data. The
implementation of the writer determines what level of concurrency
it uses, whether it checks for conflicts, and how it handles any
conflicts it encounters. The reader in the
RIOptimisticProvider,
an implementation of the
RowSetReader
interface, reads data from a relational
database. The writer, an implementation of the RowSetWriter
interface, checks for conflicts and does not write data to the data
source if there is a conflict. The reader and writer implemented in
the RIXmlProvider
read and write a rowset as an XML
document. For this reason, it is primarily used by a
WebRowSet
object. The writer does not check for
conflicts and simply writes all of a RowSet
object's
modifications to the data source.
The TransactionalWriter
interface allows a CachedRowSet
object that is
participating in a global or local transaction to get fine-grained
control of the transactional boundaries. To get this functionality,
a CachedRowSet
object needs to use a
SyncProvider
object that implements the
TransactionalWriter
interface.
The reference implementation provides a
means by which an application can choose to resolve conflicts on a
case by case basis. After the writer finds all the conflicts, it
throws a SyncProviderException
object. An application
can catch that exception and use it to create a
SyncResolver
object, a specialized kind of
RowSet
object. A SyncResolver
object
mirrors the RowSet
object experiencing the conflicts,
having the same number of rows and columns; however, it contains
only the data that is in conflict.
The SyncResolver
object
retrieves each conflict value in a row, comparing the value in the
data source with the value in the RowSet
object. After
a decision is made as to which value should persist for each
conflict in the row, the SyncResolver
object sets
those values with the method setResolvedValue
. The
SyncResolver
object then goes to the next conflict and
repeats the process until there are no more conflicts.
CachedRowSet
interface, being disconnected and able to
operate without a driver, is designed to work especially well with
a thin client for passing data in a distributed application or for
making a result set scrollable and updatable. Many other
RowSet
implementations can be designed for other
purposes.
WebRowSet
interface extends
the CachedRowSet
interface and therefore has all of
the same capabilities. What it adds is the ability to read and
write a rowset in XML format. A WebRowSet
object uses
a WebRowSetXmlReader
object to read a rowset in XML
format and a WebRowSetXmlWriter
object to write a
rowset in XML format. The XML version of a WebRowSet
object contains its metadata, including its properties, in addition
to its data.
The default constructor for a
WebRowSet
requests an RIXmlProvider
implementation so that the WebRowSet
object will be
able to read and write a rowset as an XML document. It is also
possible to set a WebRowSet
object with a third-party
SyncProvider
implementation that provides XML
capabilities.
A WebRowSet
object is
designed to work well in a distributed client/server application.
It is similar to a CachedRowSet
implementation in that
both connect a thin client to an application server. Thus, they are
both good for providing data to a thin client. The difference is
that they use different protocols. Because a
CachedRowSet
object is serializable, it can be sent to
another component using RMI/IIOP (Remote Method Invocation/Internet
Interoperability Protocol). A WebRowSet
object uses
HTTP/XML (Hypertext Transfer Protocol/eXtensible Markup Language)
to communicate with the middle tier, so that, for example, Web
clients can talk to Java servlets that provide data access.
XML has become more and more important
for Web services because of the portability of data it provides.
The JDBC RowSet
Implementations specification includes
a standard WebRowSet
XML Schema, available at
http://java.sun.com/xml/ns/jdbc/webrowset.xsd
, to
which a standard WebRowSet
object adheres. This means
that if two parties have the XML schema for a
WebRowSet
object, they can use it to exchange rowsets
in a common format even though they may store their data internally
in entirely different formats. This makes a WebRowSet
object a powerful tool for data exchange.
Because the Java platform provides portability of code and XML provides portability of data, they are the ideal combination for Web services. Technologies like JavaTM API for XML-based RPC, SOAP with Attachments API for JavaTM, JavaTM Architecture for XML Binding, and JavaTM API for XML Registries make developing Web services easier and easier. Plus the infrastructure that the J2EE platform provides saves developers from having to program their own "plumbing" for the management of distributed transactions, connection pooling, and security.
Being able to use a
WebRowSet
object for sending data adds even more to
the value of using the Java platform for Web services. For example,
because it uses the standard XML schema, a WebRowSet
object can be part of a Web services message (generally using SOAP
with Attachments API for Java technology).
The following code fragment is a simple
example that creates the WebRowSet
object
wrs
, populates it with the data of the
ResultSet
object rs
, and then updates a
column value. The final two lines output wrs
in XML
format.
WebRowSet wrs = new WebRowSetImpl(); wrs.populate(rs); //perform updates wrs.absolute(2) wrs.updateString(1, "newString"); FileWriter fWriter = new FileWriter("/share/net/output.xml"); wrs.writeXml(fWriter);The machine that receives the XML output will do something similar to the following to read it. Once
wrs
has read in the data and metadata in
fReader
, the code operates on it just as it would on
any other WebRowSet
object.
WebRowSet wrs = new WebRowsetImpl(); FileReader fReader = new FileReader("/share/net/output.xml"); wrs.readXml(fReader); wrs.absolute(2); String str = wrs.getString(1);
FilteredRowSet
object, an
extension of a CachedRowSet
object, lets a programmer
use a filtered subset of data from a rowset. For example, suppose a
FilteredRowSet
object contains a fairly large set of
rows. A programmer who wants to perform operations on only a subset
of those rows can specify a range of values, and the
FilteredRowSet
object will return only values in that
range. This filtering is done by the method next
,
which is implemented to skip any rows that do not fall within the
specified range. Without this capability, the programmer would have
to make a connection to the data source, get a
ResultSet
object with the selected rows, and populate
a rowset with those rows. Because establishing a connection is very
expensive, far outweighing the cost of the memory needed to store a
FilteredRowSet
object, the ability to get a selected
set of rows without making a new connection often results in a
significant improvement in performance.
Suppose you have a
FilteredRowSet
object frs
that contains
all of the employees in Company XYZ. You have figured the average
salary for the company, including everyone in the average. Now you
want to get information about only the employees whose names range
from Aaronson to Lee. The following code fragment causes
frs
to make available only the rows where the values
in the column NAMES
are in the range of Aaronson to
Lee.
In the following code fragment, the
FilteredRowSet
object frs
is populated
with data from the ResultSet
object rs
.
Then the code creates a Range
object, specifying that
the last names Aaronson through Lee, which are in the column
NAME
, make up the range of names that the method
next
can return.
FilteredRowSet frs = new FilteredRowSetImpl(); frs.populate(rs); Range names = new Range("Aaronson", "Lee", findColumn("NAME")); frs.setFilter(names); while (frs.next()) { String name = frs.getString("NAME"); . . . // add each name to, for example, a mailing list // only names from "Aaronson" to "Lee" will be returned }If some of the data in the subset stored in a
FilteredRowSet
object needs to be changed, it can be
modified and synchronized with the data source. Only the
modifications in the subset will be synchronized. If, however, the
source of the data is an SQL VIEW
that is not
updatable, indicated by the constant
SyncProvider.NONUPDATABLE_VIEW_SYNC
, the modifications
to the FilteredRowSet
object will not be synchronized.
JoinRowSet
object lets a
programmer combine data from two different RowSet
objects. This can be especially valuable when related data is
stored in different data sources. Any RowSet
implementation can participate in a join, but it is typically two
CachedRowSet
objects that are joined. With all of the
relevant data combined into one JoinRowSet
object, an
application can process the data just as it would for any other
kind of RowSet
object.
The following code fragment demonstrates
how the data from two CachedRowSet
objects is joined
into one JoinRowSet
object. In this scenario, data
from the table EMPLOYEES
is joined with data from the
table BONUS_PLAN
, thereby giving information about
each employee and his or her bonus plan all in one rowset. In both
of the original rowsets, the first column is the employee
identification number, so that is the column used to match the data
from one table with data from the other. After the join, each row
in the JoinRowSet
object will contain the columns from
both rowsets that pertain to the same employee ID.
The first line of code creates the
JoinRowSet
object jrs
. Next, all the
columns from the table EMPLOYEES
are used to populate
the new CachedRowSet
object empl
, the
first column of empl
is set as the match column, and
empl
is added to jrs
. Then, in similar
fashion, all the columns from the table BONUS_PLAN
are
used to populate the CachedRowSet
object
bonus
, the first column of bonus
is set
as the match column, and bonus
is added to
jrs
. In both empl
and bonus
,
the first column is EMPLOYEE_ID
, which is the primary
key. It is the column both rowsets have in common, and it can be
used to match the information from empl
about an
employee with the information in bonus
about the same
employee. The last lines of code navigate jrs
and
retrieve a column value.
JoinRowSet jrs = new JoinRowSetImpl(); ResultSet rs1 = stmt.executeQuery("SELECT * FROM EMPLOYEES"); CachedRowSet empl = new CachedRowSetImpl(); empl.populate(rs1); empl.setMatchColumn(1); // The first column is EMPLOYEE_ID jrs.addRowSet(empl); ResultSet rs2 = stmt.executeQuery("SELECT * FROM BONUS_PLAN"); CachedRowSet bonus = new CachedRowSetImpl(); bonus.populate(rs2); bonus.setMatchColumn(1); // The first column is EMPLOYEE_ID jrs.addRowSet(bonus); // The jrs instance now joins the two rowsets. The application // can browse the combined data as if it were browsing one single // RowSet object. jrs.first(); int employeeID = jrs.getInt(1); String employeeName = jrs.getString(2);Similar to the case with
FilteredRowSet
objects, a JoinRowSet
object can have its modified data synchronized providing that the
source from which the data came is not a non-updatable SQL
VIEW
.
[Top] [ Prev] [Next]
Copyright © 1993, 2010, Oracle and/or its affiliates. All rights reserved. Please send comments using this Feedback page. |
Java Technology |