Programmer's Guide



Chapter 8   Using Data Source Business Objects

A SilverStream data source object (DSO) is a triggered business object that extends the SilverStream data access model. This chapter includes the following topics:

About data providers   Top of page

Data source objects are part of the Data providers layer in the SilverStream data access architecture.

    For an overview and diagram of this architecture, and a description of SilverStream data cache objects, see Data Access Basics.

Rather than communicating directly with the data provider, the client communicates indirectly through the data cache object (AgxData). Data providers are responsible for the connection between the data cache object and the data source. The data cache may be directly connected to the data provider, in cases where the client and data cache are located on the SilverStream Server, or the data cache may have a network connection to the data provider, if the client and data cache are located remotely. In either case, the data provider implements a well-known interface (AgiDataSource) for the use of the data cache.

Connection to the data cache

The interfaces implemented by the data provider for the benefit of the data cache are different from the interfaces provided by the data cache to its clients. The data cache interfaces provide the client-level API of the SilverStream data access architecture and are intended to be easy to use and to provide a convenient way to bind form and page controls to data. On the other hand, the data provider interfaces are intended to be easy for a wide variety of data providers to implement.

The following lists the capabilities of data providers:

About data source objects   Top of page

When a SilverStream-provided data cache (AgxData) uses a SilverStream data set as its data provider, the form, page, or business object with which the AgxData is associated does not need to explicitly attach the data cache to the data provider, or to initialize the data provider. This initialization and connection is provided automatically by the SilverStream runtime environment. However to connect a data cache to a "custom" data provider (like an Enterprise Data Connector) the client must explicitly perform an initialization step to specify the data provider to be used, initialize it, and connect the data cache to the data provider.

This initialization and connection between the data cache and the data provider is done through a data source object (DSO) following this sequence:

  1. The client specifies the name of the data source object that will be invoked to initialize the data provider and connect it to the data cache.

  2. The client triggers the data source object, by running its invokeQuery() method, optionally passing it some information to be used in the data provider initialization.

  3. The data source object creates the data provider, initializes it, and connects it to the client's data cache.

Thereafter, the data source object is no longer involved: the client uses the data cache as through the standard data cache API.

SilverStream provides some utility objects that make it easier for you to produce data providers for commonly used scenarios. Each of these is discussed in detail in this chapter. They are summarized below.

ExecuteSQL DSO

In many cases you might need your data provider to provide the results of a particular SQL query against a SilverStream-supported relational database. For example, you might want to execute a SQL stored procedure, or perform some specialized query not available though the standard SilverStream expression syntax.

SilverStream supplies a utility method, to be called by a customer-written data source object, that dynamically constructs a data provider to execute a supplied SQL statement against a specified database. The ExecuteSQL DSO is relatively easy to implement, and it has the following requirements:

    For more information, see Using an executeSQL DSO.

SetResultSet DSO

SilverStream supplies another utility method, also to be called by a customer-written data source object, that dynamically constructs a data provider connected to an arbitrary java.sql.ResultSet. You might use this DSO for any of the following reasons:

Like the ExecuteSQL object, this data provider provides read-only access.

    For more information, see Using a SetResultSet DSO.

Pass-through DSO

In another common situation, you can dynamically connect a client's data cache to a standard SilverStream data set, with the connection between the data cache and the data set to be explicitly made at runtime. The reasons you might want to do this are as follows:

    For more information, see Using a pass-through DSO.

Custom DSO Implementation

To obtain the greatest flexibility, you can write your own implementation of the data provider interfaces, and supply a DSO to initialize the data provider object and connect it to a client's data cache. A typical data provider consists of two primary Java classes, one implementing the AgiDataSource interface for performing queries and returning data to the client, and one implementing the AgiDataUpdateRequest interface for gathering up a set of updates and performing them as a unit.

Most data providers also provide some auxiliary Java classes implementing SilverStream-defined interfaces like AgiBandDescriptor and AgiTransactionHandle, which are used by the primary classes and the data caches in well-specified ways. The data provider may also include private classes, native code, DLLs, or whatever is required to obtain, format, and return the data.

    For information, see About the setDataSource DSO.

How the DSO works   Top of page

This section describes the components that a DSO uses to populate an AgcData, AgpData or AgaData control.

  1. The client invokes the DSO by calling the invokeQuery() method. You bind the AgcData, AgaData, or AgpData objects (referred to generically as AgxData) to the DSO at design time or dynamically at runtime.

  2. The server notifies the DSO of the request for data from the client. It passes an instance of the AgoDataSourceEvent object into the invokeQuery event of the DSO.

  3. The invokeQuery event calls the appropriate method for the DSO implementation:

  4. When the DSO returns control to the calling object, the calling program must call the AgxData's gotoFirst(), gotoNext(), or gotoLast() method to start the download of data from the server-side data provider.

    NOTE   You cannot set an initial data mode for a data cache object bound to a DSO.

  5. After the download of data to the client and any related processing is completed, SilverStream destroys the server-side data provider objects (that can be the java.sql.ResultSet and the AgiDataSource objects).

Creating a DSO   Top of page

When you create a DSO using the SilverStream Business Object Designer, SilverStream adds the "implements AgiDataSourceListener" clause to the object's class definition, and it registers the object as a data source listener. Like all business objects, data source listeners are stored in a database and run on the server.

The following describes the general procedure for creating a DSO. For more information, see Business Object Designer in the online Tools Guide.

  1. Create the DSO using the Business Object Designer by selecting the Data Source trigger.

    If you know the column definitions and want to make them available for design-time data binding, you can specify the column definitions.

  2. Code the DSO's invokeQuery event:

  3. Code the DSO to call the setResult() method.

  4. Create a form, page, or business object and add an auxiliary data set (AgxData).

    Either set the datasource to the DSO using the Business Object Designer, or call setDataSource() at runtime.

  5. Code the calling object to call the invokeQuery() method to invoke the DSO.

Importing a DSO created externally   Top of page

You can create a DSO outside of the Business Object Designer, then import the source or class file using the SilverCmd ImportSource and ImportClass commands.

    For information see the SilverCmd Reference in the online Tools Guide.

Using an executeSQL DSO   Top of page

To write your own SQL statements, ranging from simple SELECT statements to a very complex, nested, and dynamically generated statements, you can create a DSO and pass the desired SQL to the database in the form of a Java String. You do this by calling executeSQL() on the event object. This is a common way to use a stored procedure to populate an AgData. SilverStream creates, manages, and destroys the objects that are required for this functionality.

The important method calls in this implementation include AgxData.invokeQuery(), evt.executeSQL(), and evt.setResult().

When writing SQL for this implementation, note the following:

Defining the executeSQL DSO   Top of page

Use the Business Object Designer to create the DSO as described in the section Creating a DSO. You must call several methods within the DSO's invokeQuery event, as described in the procedure that follows. The invokeQuery event receives an AgoDataSourceEvent as a parameter.

  1. Obtain any parameters passed to the DSO from the caller. Use the evt.getParameter() method, which has this declaration.

      Serializable getParameter() 

    You might have to cast the parameter to a useful data type because getParameter() returns Serializable.

    Depending on the contents of the parameter, you can use it to construct your SQL statement (for example, if you want to construct a statement dynamically based on the user input, which the invokeQuery() method passes). The parameter might also be the SQL statement itself.

  2. Call the evt.executeSQL() method, which does the following:

  3. Call the setResult() method to return a value to the calling program. It has this declaration.

      void setResult(Serializable result) 

Example code for the DSO invokeQuery event   Top of page

The example below shows code for the invokeQuery event on an executeSQL DSO. The database name and SQL statement are passed from the caller as a hashtable. The code gets the hashtable info, casts the data to Strings, then passes them to executeQuery(). Finally the code calls setResult().

  public void invokeQuery(AgoDataSourceEvent evt) throws Exception 
{
AgiDatabase dbObject = null;  // The database to connect to.
Hashtable hshQueryInfo = null;// Hashtable with query info.
// Get the SELECT statement and database from caller.
hshQueryInfo = (Hashtable) evt.getParameter();
// Get the Hashtable parameters and cast them to Strings.
String sQuery = (String) hshQueryInfo.get("Query");
String sDatabase = (String) hshQueryInfo.get("Database");

// Get a database object.
dbObject = evt.getServer().getDatabase(sDatabase);
// If successful, Execute the SQL statement for this DB

if (dbObject != null)
{
evt.executeSQL(dbObject, sQuery);
// Return success.
evt.setResult("Success");
}
else
{
// Return failure.
evt.setResult("Failed to get database: " + sDatabase);
}
}

    For more information about this example, go to Application Techniques>Triggered Data Source Objects in the help system: Using an executeSQL DSO.

Calling a stored procedure   Top of page

Since a stored procedure is a SQL construct, you follow the same steps to call a stored procedure as you do to execute any SQL statement. You pass the call to the stored procedure using the executeSQL() method. Here's an example of a call to a stored procedure:

  evt.executeSQL("db-name", "CALL stored_proc_name") 

where

NOTE   The stored procedure must reside in the database you are calling.

Using a SetResultSet DSO   Top of page

You can create a data provider object that uses the java.sql.ResultSet object, and a DSO that returns it. This type of data provider allows you to access any type of data, not just relational databases through SQL. Note that it does not allow you to update the data source. The SilverStream Server calls the java.sql.ResultSet methods as necessary and destroys the objects when it no longer needs them.

The important method calls for this implementation include: AgxData.invokeQuery(), evt.setResultSet(), and evt.setResult().

About the java.sql.ResultSet interface   Top of page

When the SilverStream Server receives a request for a data provider, it internally calls certain methods on the java.sql.ResultSet object (see the list of methods below). This means that the ResultSet object must implement a specific subset of java.sql.ResultSet and java.sql.ResultSetMetaData methods. Note that even if the object you use implements other methods, the server does not call them.

The java.sql.ResultSet interface provides a row/column dataset to an AgiDataSource object. The AgiDataSource object then provides the data to the requesting SilverStream data cache object (such as AgcData, AgpData, and so on). JDBC drivers return datasets in this standard Java format.

NOTE   You cannot use objects that implement java.sql.ResultSet to update the external data source.

ResultSet methods called by the server

The SilverStream Server calls the java.sql.ResultSet methods listed in this table.

Method

Description

getMetaData()

Descriptor methods to get column names and number of columns, and so on.

close()

Frees resources.

next()

Positions the cursor.

getObject(int)

Returns the actual data for a given column.

ResultSetMetaData methods called by the server

The SilverStream Server calls the java.sql.ResultSetMetaData methods listed in this table.

Method

Description

getColumnCount()

Returns the number of columns.

getColumnName()

Returns the column name.

getColumnType()

Returns the column's data type.

getPrecision()

Returns the column's precision.

getTableName()

Returns the name of the table.

isReadOnly()

Returns True when the column is read-only.

isNullable()

Returns True when the column supports null values.

isAutoIncrement()

Returns True when the column is an auto-increment column.

Defining the java.sql.ResultSet DSO   Top of page

You must call several methods within the DSO's invokeQuery event, which is passed an AgoDataSourceEvent as a parameter. The following procedure describes how to code the DSO.

  1. Get any parameters passed to the DSO from the caller using the evt.getParameter() method which has this declaration.

      Serializable getParameter() 

    You might have to cast the parameter to a different data type since getParameter() returns Serializable.

  2. Instantiate the object that implements the java.sql.ResultSet passing any necessary parameters.

  3. Call the setResultSet() method. The setResultSet() method notifies the server that it should get data from the specified data provider (the java.sql.ResultSet). It has two variants:

  4. Call the setResult() method to return a value to the calling program, which has this declaration.

      void setResult(Serializable result) 

Example code for instantiating the ResultSet object   Top of page

This example instantiates ObjTabDelimitedSQLFile, a utility object that implements the result set. The example uses the executeQuery() method and passes the sFilename and sQueryParm variables. The executeQuery() method is implemented by the ObjTabDelimitedSQLFile (the java.sql.ResultSet object).

  public void invokeQuery(AgoDataSourceEvent evt) throws Exception 
{
// Instantiate the business object that implements the
// SQL.resultSet
ObjTabDelimitedSQLFile fileData = new ObjTabDelimitedSQLFile();
// get the data from the hashtable passed to the DSO from the
// calling form.
Hashtable hshParm = (Hashtable) evt.getParameter();
String sFilename = (String) hshParm.get ("file");
String sQueryParm = (String) hshParm.get ("queryparm");
try
{
// specify the result set
evt.setResultSet (fileData.executeQuery(sFilename,
sQueryParm));
evt.setResult ("Success");
}
catch (Exception e)
{
System.out.println (e.toString());
throw(e);

}
}

    For more information about this code and other objects used in this implementation, go to Application Techniques>Triggered Data Source Objects in the help system: Using a setResultSet DSO.

Using a pass-through DSO   Top of page

A pass-through DSO is a type of triggered business object defined by SilverStream to provide a data set for SilverStream or non-Silverstream clients. There are two primary reasons for using a pass-through DSO:

Creating a pass-through DSO   Top of page

  1. Use the Business Object Designer to define a Data Source triggered business object.

  2. Check pass-through DSO in the Triggers Property sheet.

  3. Define a data set using the Data Object Property sheet.

The following describes how SilverStream sets up the pass-through DSO.

Creating a pass-through DSO programmatically   Top of page

It is not necessary to use the Designer to define the pass-through parameters. You might want to create the DSO programmatically to provide more flexibility. For example, you could define multiple AgaData's, and dynamically choose which one to set as its data source at runtime. This could be based, for example, on a parameter passed in to the object's invokeQuery() method.

Follow these steps to create the pass-through parameters programmatically:

  1. In the Business Object Designer, create a DSO, but do not mark it as pass-through.

  2. Define one or more AgaData's on that DSO, and define the datasets for them. Note the following restrictions when creating the datasets:

  3. Use the Designer to define the columns to be returned from this DSO.

    NOTE   This is required only if the DSO is to be used in the Form or Page wizard.

  4. In the invokeQuery() method for the DSO make the following call:

      evt.setDataSource(yourAgaData.detachDataSource()); 

    where yourAgaData is a local variable defined like this:

    AgaData yourAgaData = evt.getAgaData("yourAgaData");

About the setDataSource DSO   Top of page

You can create a custom DSO that can both access and update data from any data source.

Note that this data provider interface is somewhat complex, and implementing it correctly requires a substantial investment of effort. The next two sections provide separate descriptions for accessing and updating data.

     For access to a documented example of a setDataSource DSO, go to Application Techniques>Triggered Data Source Objects in the help system: Using a setDataSource DSO.

Using a setDataSource DSO to access data    Top of page

You need to create the following objects to access the data source.

data source object

A SilverStream data object that implements AgiDataSourceListener.

AgiDataSource

The data provider object that implements the AgiDataSource interface. It obtains the data from the external data source and formats it in the appropriate row/column format and populates the server-side data buffer for download to the client.

Typically, your DSO should not implement AgiDataSource directly. Instead, you can instantiate your own data provider object that implements AgiDataSource. This allows your DSO to serve multiple clients.

For more information, see Implementing AgiDataSource.     

AgiBandDescriptor

A utility class that implements the AgiBandDescriptor interface. It provides important column information.

For more information, see Implementing AgiBandDescriptor    

Data access flow    Top of page

Fetch requests occur when an AgxData on the calling object calls one of these methods: query(), gotoFirst(), gotoNext(), gotoLast(), or refreshRows(). The SilverStream Server receives the request and in turn calls the appropriate AgiDataSource method.

The following diagram illustrates the flow of a fetch request for an AgcData control that is bound to a DSO whose data provider implements AgiDataSource. (The data flow is similar for other AgxData objects.)

  1. The AgxData object that is bound to a DSO calls invokeQuery().

  2. The DSO invokeQuery event instantiates the object that implements AgiDataSource and sets it as the data source by calling the setDataSource() method.

  3. The Server sets the AgiDataRowFactory object. The AgiDataRowFactory creates a data row from an array of Objects (columns).

  4. The form or page control calls one of the navigation methods (gotoXXXX()) on the AgxData to start the download of data from the server.

    Any subsequent calls to gotoXXXX() are made to the local cache of data.

  5. During the initial download, the AgxData control gets a description of the data from the server. The server gets the description of the data from the AgiDataSource object, by calling the getColumnxxx() methods on the AgiDataSource object.

  6. The AgxData control starts a background thread that downloads the remaining data from the AgiDataSource. This results in calls to getNextRows() on the AgiDataSource.

The next two sections provide more information about how to implement the necessary methods and protected variables to access data.

Implementing AgiDataSource   Top of page

Do the following to create this object:

  1. Use the Business Object Designer to create an object.

  2. When prompted by the Designer Wizard, specify com.sssw.rt.util.AgiDataSource as an interface that you want to implement.

    You can also specify that the Wizard create stubs for all of AgiDataSource's methods. When you specify "create stubs", the Wizard creates separate methods. The methods are then available through the event drop-down in the Object Designer.

Note that an AgiDataSource has state, for example, a current query, a current row position, and so on. When you implement an AgiDataSource, you must ensure that each client (AgxData) has its own AgiDataSource. Otherwise you cannot guarantee that the state would be correct, thereby causing inconsistent or unexpected results.

This following lists the methods in the AgiDatabase object that you must implement.

checkException()

Checks to see if there is a single buffered exception on the specified data source. An exception might be buffered because the data source can run asynchronously.

NOTE    If the data source does not run asynchronously, typically this method should do nothing, because other class methods can throw exceptions.

This method throws an AgoApiException if an exception was buffered for later delivery. It has this declaration:

  void checkException() throws AgoApiException 

close()

Closes the data source and frees any resources associated with the current query. The close() method should protect itself from being called more than once. The client may call the reQueryIfClosed() method to restart it. It has this declaration:

  void close() 

getBandDescriptor()

Returns a "band description" for this data source. The band descriptor describes the contents of the data rows used in it. For example, it describes the number of columns, the name and type of each column, and so on. It has this declaration:

  AgiBandDescriptor getBandDescriptor() 

NOTE    The AgiBandDescriptor provides analogous functionality to the java.sql.ResultSetMetaData interface.

getNextRows()

Returns an Enumeration of rows (of type AgiDataRow). The Enumeration represents the next set of rows from the AgiDataSource. It returns an empty enumeration if no more rows are currently available, but when more rows might be available later. It can also return fewer than count rows.

The getNextRows() method does not start the query if it has not already been started, or if the AgiDataSource was closed. The client calls reQueryIfClosed() first if the AgiDataSource was closed. The AgiDataSource should construct the rows by calling the AgiDataRowFactory object specified by a call to the setDataRowFactory() method.

The getNextRows() method should throw an AgoEndOfRowsException when the end of the query has been reached. It has this declaration:

  Enumeration getNextRows(int count)  
throws AgoEndOfRowsException, AgoApiException

Where count is the number of rows to be returned.

prepareUpdateRequest()

When updating data, this method prepares an update request to be executed against this data source. The update request can then be "filled" with rows to be inserted, deleted, updated, or published, and then executed. If the update request is not executed, it should be closed. You must implement an AgiDataUpdateRequest object if you intend to update data through this AgiDataSource. It has this declaration:

  AgiDataUpdateRequest prepareUpdateRequest() throws 
AgoApiException

    For more information, see Using a setDataSource DSO to update data.

readOnly()

Returns true if the data source is marked as read-only, which means that no updates are allowed. It has this declaration:

  boolean readOnly() throws AgoApiException 

reQuery()

Re-executes the current query on the data source, closing the query in progress if any. SilverStream calls this method when the AgxData control calls refreshRows(). It throws the AgoApiException if the AgiDataSource is closed or when other errors occur. It has this declaration:

  void reQuery() throws AgoApiException 

reQueryIfClosed()

Re-executes the current query on the data source if and only if the data source has been closed. It has this declaration:

  void reQueryIfClosed() throws AgoApiException 

setDataRowFactory()

Sets the AgiDataRowFactory that this AgiDataSource uses for constructing data rows. The AgiDataSource must use the AgiDataRowFactory when constructing rows returned from the getNextRows() call. The server guarantees it will call this method before calling any methods that might return rows (such as getNextRows() or prepareUpdateRequest(). It has this declaration:

  void setDataRowFactory(AgiDataRowFactory rowFactory) 

Where rowFactory is the factory object to be used for constructing rows. (You must save the row factory for use by the getNextRows() method.

startNewQuery()

Starts a new query on the data source, closing the old query if any. The server calls this method in response to a query() or refreshRows(), or the initial call to gotoFirst(), gotoNext(), or gotoLast(). It should throw an AgoApiException if the data source was closed or other error occurred. It has this declaration:

  void startNewQuery(String queryString,String orderBy, 
int fetchAheadCount) throws AgoApiException

Where:

NOTE   Most data sources do not support the fetch-ahead feature, and therefore ignore this parameter.

Implementing AgiBandDescriptor    Top of page

This interface describes the columns provided by an AgiDataSource object. Do the following to create this object:

  1. Use the Business Object Designer to create an object.

  2. When prompted by the Designer Wizard, specify com.sssw.rt.util.AgiBandDescriptor as an interface that you want to implement.

This section lists the methods you must implement in the AgiBandDescriptor object.

canQueryColumn()

Returns true if the specified column's value can be used in queries. Certain column types, such as LONGVARCHAR, are not allowed in queries. Data binding support does not require this method. It has this declaration:

  boolean canQueryColumn(int index) 

Where index is the column index. Columns numbers are zero-based.

getBandName()

Returns the name of the band. If there is only one band, this method can return null, indicating that the data binding mechanism should not check the band name. (Normally, a DSO will return null.) It has this declaration:

  String getBandName() 

getColumnCount()

Returns the number of columns provided by this band.It has this declaration:

  int getColumnCount() 

getColumnName()

Returns the name of a column. It has this declaration:

  String getColumnName(int index) 

Where index is the index of the column. Column numbers are zero-based.

getColumnType()

Returns the data type constant from com.sssw.rt.util.DatatypeCodes of a column. It has this declaration:

  char getColumnType(int index) 

Where index is the column index. Columns numbers are zero-based.

getPrimaryTableName()

Returns the name of the primary table for this band. The primary table is the table that can be updated by changes to the columns in this band. Data binding support does not require this method. It has this declaration:

  String getPrimaryTableName() 

Using a setDataSource DSO to update data   Top of page

To create an updateable DSO you need to implement an AgiDataUpdateRequest object. As described previously, the AgiDataUpdateRequest object performs the work of executing data changes to the data source. There are two ways you can implement this object:

If you subclass AgoDataUpdateRequest, you must also create objects that implement AgiDataUpdateRow and AgiTransactionHandle. For many situations the combination of AgoDataUpdateRequest, AgoDataUpdateRow, and AgiTransactionHandle are convenient because they handle the mechanics of updating rows (for example, starting and committing transactions).

    For information about implementing AgiDataUpdateRequest directly, see Implementing AgiDataUpdateRequest directly.

First you must implement the data access DSO, as described in the previous section. The following lists the remaining objects you need to implement.

AgoDataUpdateRequest

A utility class that acts as an "update holder." It extends the AgoDataUpdateRequest object (which implements AgiDataUpdateRequest.) It gathers the updated rows from the AgcData on the client and contains methods that define how the updates should be committed to the external datasource.

For more information, see Subclassing AgoDataUpdateRequest.

AgoDataUpdateRow

A utility class that acts as a row holder. It extends the AgoDataUpdateRow object, and builds the appropriate SQL to handle the requested data source operation.

For more information, see Subclassing AgoDataUpdateRow.

AgiTransactionHandler

A utility class that implements the AgiTransactionHandle interface. It connects and disconnects from the database.

For more information, see Implementing AgiTransactionHandle.    

Data update flow   Top of page

The following diagram illustrates the flow of an update request for an AgxData control. For this illustration, it is assumed that invokeQuery(), setDataSource() and gotoXXXX() or query() methods have already been called, as described in the previous section.

  1. Data source update requests occur when the caller calls the updateRows() method on the data cache (AgxData.)

  2. The SilverStream Server receives the request and calls the appropriate AgiDataSource method prepareUpdateRequest().The method is called on the data provider object connected to the data cache.

  3. The prepareUpdateRequest() instantiates an "update holder" object that implements AgiDataUpdateRequest object and, in most cases, extends AgoDataUpdateRequest.

  4. The SilverStream Server then calls methods on the AgiDataUpdateRequest object, as described in the next section.

How the update holder object works

When the update holder object is instantiated, the SilverStream Server scans the data cache for modified rows. It scans the rows in the order they were modified, as described in Updating data. The following describes how the modified rows are processed.

  1. As the scan finds each modified row, it calls one of the following methods on the update holder object:

      insertRow() 
    updateRow()
    deleteRow()
  2. The update holder creates a vector of rows by calling createUpdateRow() in each of these methods. The createUpdateRow() method in turn creates a new "row holder" object each time it is called. (This is the object that extends AgoDataUpdateRow.) When finished, the update holder has a vector of row objects in the order they were modified.

  3. The SilverStream Server calls executeAllUpdates() on the update holder object. This method is implemented by the base class AgoDataUpdateRequest, so you do not need to implement it yourself. It does the following:

  4. Finally, the excecuteAllUpdates() method gathers up any modified rows returned by prepareAndExecuteUpdate() and returns an enumeration to the data cache, so that it can update its cached values.

The remainder of this chapter provides reference information about the methods and protected variables you must implement to update data.

Subclassing AgoDataUpdateRequest   Top of page

AgoDataUpdateRequest is an abstract base class implementing the com.sssw.rt.util.AgiDataUpdateRequest interface. You can use it as a base class when you are writing updateable DSOs. The com.sssw.rt.util.AgoDataUpdateRequest acts as a holder for gathering up rows to be updated, and then executing the entire batch as a single database transaction. It also defines methods that handle the transaction mechanics for updating a relational database, such as createDataUpdateRows(), beginTransaction(), commitTransaction(), and abortTransaction().

AgoDataUpdateRequest variables

AgoDataUpdateRequest includes the following protected variables, which may be accessed by derived classes.

m_rowfactory

The factory for creating AgiDataRows. This factory must be used for creating any rows returned in the "server-modified rows" enumeration.

m_rowupdate

The vector of update rows, of type AgoDataUpdateRow.

AgoDataUpdateRequest methods

AgoDataUpdateRequest includes the following methods:

abortTransaction()

Abort the transaction against the "database" associated with this data update request. Throws an exception if an error occurs aborting the transaction. It has this declaration:

  void abortTransaction(AgiTransactionHandle transactionHandle) throws AgoApiException 

Where transactionHandle is the handle returned from the beginTransaction.

AgoDataUpdateRequest()

The constructor is normally called only from the DSO's prepareUpdateRequest() method. It takes a 'row factory that was passed to the business object. This is used to construct new data rows to be returned in the enumeration from executeAllUpdates (containing modified data).

  AgoDataUpdateRequest(AgiDataRowFactory rowFactory) 

Where rowFactory is the data row factory for modified rows.

NOTE   The constructor should call super(rowfactory); as its first statement.

beginTransaction()

Starts a transaction against the "database" associated with this data update request. Throws an exception if an error occurs starting the transaction. Returns a "transaction handle" for this transaction. It has this declaration:

  AgiTransactionHandle beginTransaction()  
throws AgoApiException

checkUpdates()

Check the update request(s) for validity. This includes setting the values of any server-assigned fields, and running any validation expressions. Normally, the default implementation of this method in AgoDataUpdateRequest, which calls the checkStatement() method of each row in the update, will suffice, but it is documented here in case the user-written derived class needs to override or augment it. Throws exception if error occurs during validation phase. It has this declaration:

  void checkUpdates() throws AgoApiException 

commitTransaction()

Commit the transaction against the "database" associated with this data update request. Throws an exception if an error occurs committing the transaction. It has this declaration.

  commitTransaction(AgiTransactionHandle transactionHandle) 
throws AgoApiException

Where transactionHandle is the handle returned from the beginTransaction.

createUpdateRow()

Create a "row holder" object for the specified row. Returns a derived class of AgoDataUpdateRow. The row holder must hold the type of update (insert, delete, or makePrimary) and the row itself. All derived classes will need to implement this method by constructing an appropriate derived class of AgoDataUpdateRow that knows how to update itself. It has this declaration:

  AgoDataUpdateRow createUpdateRow(char kind, 
AgiDataSource dataSource,AgiDataRow row)
throws AgoApiException

Where:

createUpdateRow()

Create a "row holder" object for the specified row. Returns a derived class of AgoDataUpdateRow. The row holder must hold the type of update (always update), the old row, and the new row. All derived classes will need to implement this method by constructing an appropriate derived class of AgoDataUpdateRow that knows how to update itself. It has this declaration.

  AgoDataUpdateRow createUpdateRow(char kind, 
AgiDataSource dataSource,AgiDataRow oldRow,
AgiDataRow newRow) throws AgoApiException

Where:

executeUpdatesInTransaction()

Execute the actual updates. A transaction has already been started. Return an Enumeration of server-modified rows in the correct format (AgiDataRow). Most derived classes will not need to override this method. Instead, they will depend on the prepareAndExecuteUpdate() method in the related subclass of AgoDataUpdateRow. It has this declaration:

  Enumeration executeUpdatesInTransaction(AgiTransactionHandle transactionHandle) throws AgoApiException 

Where transactionHandle is the handle returned from the beginTransaction().

Methods implemented from AgiDataUpdateRequest

In addition, AgoDataUpdateRequest implements the following methods from AgiDataUpdateRequest:

Subclassing AgoDataUpdateRow    Top of page

Use the Business Object Designer to subclass com.sssw.rt.util.AgoDataUpdateRow.

The com.sssw.rt.util.AgoDataUpdateRow class is an abstract base class for holding rows to be updated against a relational database or other data source. It is designed for use as a base class for updateable DSOs. This object holds the row values sent from the client, plus additional information including the data source, the kind of update, and the row factory to be used in creating a return row with server-modified data.

This object must implement the checkStatement() and prepareAndExecuteUpdate() methods to actually perform the update requested by this row.

It includes the following protected variables, which can be accessed by derived classes.

m_operation

The kind of operation (insert, delete, update, or makePrimary) from com.sssw.rt.util.CommandCodes.

m_mainrow

The data row containing the modified data. For an update request, this is the "old row" containing the pre-modification values.

m_updaterow

For an update request only, the data row containing the new values. This row is "sparse", that is, only the modified columns are present.

m_rowfactory

The row factory to be used for creating the row to be returned from prepareAndExecuteUpdates(). This row contains server-modified data, if necessary.

m_dataSource

The data source of this row.

AgoDataUpdateRow includes the following methods:

AgoDataUpdateRow()

The constructor for insert, delete, or make-primary operations. Returns a derived class of AgoDataUpdateRow. It has this declaration:

  AgoDataUpdateRow(char operation, 
AgiDataSource dataSource,AgiDataRowFactory rowFactory,
AgiDataRow row) throws AgoApiException

Where:

This constructor must call the following as its first statement:

  super (operation, datasource, rowfactory, row); 

AgoDataUpdateRow()

The constructor for update operations. Returns a derived class of AgoDataUpdateRow. It has this declaration:

  AgoDataUpdateRow(char operation, 
AgiDataSource dataSource,
AgiDataRowFactory rowFactory,
AgiDataRow oldRow,AgiDataRow newRow)
throws AgoApiException

Where:

This constructor must call the following as its first statement:

  super (operation, datasource, rowfactory, oldrow, newrow); 

checkStatement()

Check the update request for validity. This includes setting the values of any server-assigned fields, and running any validation expressions. It throws an exception if an error occurs during validation phase. It has this declaration:

  void checkStatement() throws AgoApiException 

prepareAndExecuteUpdate()

Prepare and execute the statement. Return a server-modified row created with the specified row factory, or null if there were no server modifications. Throws an exception if the execute fails.

  AgiDataRow prepareAndExecuteUpdate(AgiTransactionHandle 
transactionHandle) throws AgoApiException

This is where the data source is updated. For example, if your data provider were connected to a relational database, the prepareAndExecuteUpdate() statement would execute an INSERT, UPDATE, or DELETE SQL statement against the database, using the data from the AgiDataRow.

About rows modified externally

It is possible that the execution of prepareAndExecuteUpdate() might result in the data row being modified. For example, in a relational database, there might be an auto-increment column whose value is assigned by the database during the execution of the INSERT statement.

If the row is modified in any way, the row holder should return the value of the modified row from the prepareAndExecuteUpdate() method. It creates the modified row by using the row factory passed into the constructor.

For example, suppose column 1 contains the auto-increment field; the code might look like this:

  public AgiDataRow prepareAndExecuteUpdate( 
AgiTransactionHandle hand)
{
if (m_kind == CommandCodes.CCINSERT) {

Integer autoIncrementValue;
// insert the row to the database here
autoIncrementValue = // get the auto-increment value
// from the database
AgiDataRow modRow =
m_rowFactory.createDataRow(m_mainRow.getRowKey());
modRow.setData(1, autoIncrementValue);
return modRow;
} else ...
}

Implementing AgiTransactionHandle    Top of page

Use the Business Object Designer to create a class that implements com.sssw.rt.util.AgiTransactionHandle

An AgiTransactionHandle represents an in-progress database transaction. An AgoDataUpdateRequest uses the transaction handle to hold information that must be passed to each updateable row during the update operation. The AgoDataUpdateRequest. beginTransaction() method creates an AgiTransactionHandle.

AgiTransactionHandle currently defines no methods, but you might consider creating methods to get a Connection object and release a Connection object.

Implementing AgiDataUpdateRequest directly   Top of page

Rather than subclassing AgoDataUpdateRequest (which implements AgiDataUpdateRequest) you can write your own implementation of AgiDataUpdateRequest.

The class that implements the AgiDataUpdateRequest interface must implement the following methods.

close()

Closes the update request, discarding any pending changes. It has this declaration:

  void close() 

deleteRow()

Deletes the specified data row for the specified data source. The data source should be of the same type as the data source that created this update request. It has this declaration:

  void deleteRow(AgiDataSource dataSource,AgiDataRow row) throws AgoApiException 

where

executeAllUpdates()

Executes the updates as atomically as possible. Returns an enumeration of the rows whose values were altered during the update (for example, auto-increment rows or rows modified by a default value expression). Each row in the enumeration should have been created using the AgiDataRowFactory associated with the data source it came from. It has this declaration:

  Enumeration executeAllUpdates() throws AgoApiException 

insertRow()

Inserts the specified data row for the specified data source. The data source should be of the same type as the data source which created this update request. It has this declaration:

  void insertRow(AgiDataSource dataSource,AgiDataRow row) throws AgoApiException 

Where

makePrimaryRow()

Make the specified data row the primary version for the specified data source. The data source should be of the same type as the data source which created this update request. It has this declaration:

  void makePrimaryRow(AgiDataSource dataSource,AgiDataRow row) throws AgoApiException 

Where

updateRow()

Update the specified data row for the specified data source. The data source should be of the same type as the data source which created this update request. The oldRow identifies the row being updated, while the newRow contains the new and/or changed values. Note that the newRow will be sparse, that is, only columns that were modified by the client are present. It has this declaration:

  void updateRow(AgiDataSource dataSource,AgiDataRow oldRow,AgiDataRow newRow) throws AgoApiException 

Where






Copyright © 2000, SilverStream Software, Inc. All rights reserved.