A SilverStream data source object (DSO) is a triggered business object that extends the SilverStream data access model. This chapter includes the following topics:
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.
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:
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:
invokeQuery()
method, optionally passing it some information to be used in the data provider initialization.
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.
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.
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.
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.
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.
This section describes the components that a DSO uses to populate an AgcData, AgpData or AgaData control.
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.
evt.executeSQL()
evt.setResultSet()
evt.setDataSource()
Depending on the implementation, it might also instantiate other objects.
In addition, all DSOs call evt.setResult()
from the invokeQuery()
call to specify the value to return to the caller. This value is not the dataset. It can be anything that is useful for the program. For example, you might want to return a value that indicates success or failure.
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.
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.
If you know the column definitions and want to make them available for design-time data binding, you can specify the column definitions.
evt.executeSQL()
evt.setResultSet()
evt.setDataSource()
setResult()
method. Either set the datasource to the DSO using the Business Object Designer, or call setDataSource()
at runtime.
invokeQuery()
method to invoke the DSO.
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.
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:
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.
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.
evt.executeSQL()
method, which does the following:
The executeSQL()
method has two variants. One variant takes the database name as a String.
void executeSQL(String dbname,String s)
throws AgoApiException
Another variant takes an AgiDatabase (which you can get by calling evt.getDatabase()
) as a parameter.
void executeSQL(AgiDatabase db,String s)
throws AgoApiException
setResult()
method to return a value to the calling program. It has this declaration.
void setResult(Serializable result)
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.
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")
NOTE The stored procedure must reside in the database you are calling.
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()
.
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.
The SilverStream Server calls the java.sql.ResultSet methods listed in this table.
Descriptor methods to get column names and number of columns, and so on. | |
The SilverStream Server calls the java.sql.ResultSetMetaData methods listed in this table.
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.
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.
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:
void setResultSet(ResultSet rs)
where rs
is the java.sql.ResultSet to be returned by the event.
void setResultSet(ResultSet rs,AgiDatabase db,Connection c,Statement st)
where:
rs
is the java.sql.ResultSet to be returned by the eventdb
is the database the java.sql.ResultSet came fromc
is the connection used to get the java.sql.ResultSetst
is the statement used to get the java.sql.ResultSetFor more information about this method, see online for AgoDataSourceEvent in the SilverStream API.
setResult()
method to return a value to the calling program, which has this declaration.
void setResult(Serializable result)
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.
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:
NOTE From the non-SilverStream client you can access supported relational databases through the pass-through DSO, or access any data source using the other types of DSOs described in this chapter.
Non-SilverStream clients use AgrDatas to access a DSO, which in turn, provides the data to the client. The AgrData is an instance of AgiRowCursor/AgiSetManager that can be created at runtime, and is initialized with an established AgiServerSession.
For more information, see the SilverStream API in on-line help for AgrData, and Writing External Java Clients.
This centralizes the definition of the data set into one place, thereby simplifying maintenance.
The following describes how SilverStream sets up the pass-through DSO.
evt.setDataSource(agaPassThrough.detachDataSource());
The DSO will now appear in the relational data palettes for the Form and Page wizards, and can be used anywhere any other DSO can be used.
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:
NOTE This is required only if the DSO is to be used in the Form or Page wizard.
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");
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.
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
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.)
invokeQuery()
.setDataSource()
method.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.
getColumnxxx()
methods on the AgiDataSource object. getNextRows()
on the AgiDataSource.
refreshRows()
method on the AgxData, the server calls the reQuery()
method, which discards the contents of the AgiDataSource and re-gets the data from the external source.
The next two sections provide more information about how to implement the necessary methods and protected variables to access data.
Do the following to create this object:
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
-1
to mean "use server default." This value can be 0.
NOTE Most data sources do not support the fetch-ahead feature, and therefore ignore this parameter.
This interface describes the columns provided by an AgiDataSource object. Do the following to create this object:
This section lists the methods you must implement in the AgiBandDescriptor object.
canQueryColumn()
R
eturns 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()
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.
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.
updateRows()
method on the data cache (AgxData.)prepareUpdateRequest()
.The method is called on the data provider object connected to the data cache. prepareUpdateRequest()
instantiates an "update holder" object that implements AgiDataUpdateRequest object and, in most cases, extends AgoDataUpdateRequest.
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.
insertRow()
updateRow()
deleteRow()
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. 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:
checkStatement()
method on each row object in the vector, in order to run any validation code on the server. You implement this method in the row holder object.beginTransaction()
method on the update holder object. This method returns a transaction handle. This is the utility class you create that implements AgiTransactionHandler. It holds any transaction state you need during updates.prepareAndExecuteUpdate()
on each row in the vector, passing it the transaction handle returned in the previous step. You implement this method in the row holder object. This is where the data source is actually updated.commitTransaction()
method, passing it the transaction handle. You implement this method to commit the transaction you started with commitTransaction()
.
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.
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 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 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
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
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()
.
In addition, AgoDataUpdateRequest implements the following methods from AgiDataUpdateRequest:
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
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
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
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.
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 ...
}
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.
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
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
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
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