This chapter is about SilverStream table-modified objects, which are triggered business objects that respond to modifications on database tables. This chapter is organized as follows:
A table-modified triggered business object (or table listener) is a type of SilverStream business object that implements the AgiTableListener interface. Objects that implement this interface are registered with the application server to listen for table events, including row updates, row inserts, and row deletes. To be a registered listener means that the object tells the server that it wants to be notified when a modification occurs on a specific database table. The server uses events as the mechanism to notify the listener of a modification. Table listeners reside in a database and execute on the server.
A table listener is associated with a single database table. It programmatically responds to database insert, update, and delete operations requested by other SilverStream applications (forms, pages, or other objects).
The table listeners run within the database transaction, so you can use them to change a table's contents, and to modify, rollback, or abort the transaction in which the listener runs. You might use table listeners to do any of the following:
Table listeners do not respond to changes to the database structure (for example, the SQL ALTER TABLE statement) nor are they database triggers. The table listener event is triggered by code that runs in the SilverStream dataset, that is, any application that updates data through an AgData. This includes pages, forms, business objects, pass-through data source objects, and Enterprise JavaBeans that access data through SilverStream.
Table listeners are not triggered by other database applications that change data. For these cases, you might consider writing a database trigger to call your table listener using CORBA, RMI, or HTTP.
When you create a business object using the SilverStream Business Object Designer and specify a table-modified trigger, SilverStream adds the implements AgiTableListener clause to the object's class definition and registers the object as a table listener. SilverStream also ensures that the object is properly registered on the server.
For more information, see the chapter on Using the Business Object Designer in the on-line Tools Guide.
You can create a Java class in an external editor that implements AgiTableListener, then import the source or class file using the SilverCmd ImportSource and ImportClass commands.The import command must include an input file containing the necessary table listener attributes.
For information see the SilverCmd Reference in the online Tools Guide.
When you create a table listener, you associate it with a single database table. When a SilverStream application updates that table, the SilverStream Server instantiates (as necessary) any event-lifetime (as opposed to server- lifetime) listeners, then notifies the registered listeners of the event that occurred.
The server broadcasts the event to the listeners and passes an object with the event. The event object provides the table listener with information about the database row that was modified. It also provides access to other parts of the application server environment.
This table summarizes the components of the SilverStream table listener model.
A helper object that is available through the event object passed to the table object by the server. It provides a rich set of methods that let you change the data and commit or roll back SilverStream transactions.
A different event is fired for each database operation that occurs. This table describes the events that are fired for table listeners.
Each of these events supplies a different event object based on the kind of information that the event produces. For example, a rowInserted event supplies information about the row that is being added, while an abort event does not. All of the event objects are subclasses of AgoBusinessObjectEvent, so they also provide access to other parts of the application server environment.
Table events are produced by an event producer object that implements the AgiTable interface. The following diagram illustrates how actions from a SilverStream client application cause the server to produce these events and pass them to the table listener.
The actual row that is being written to the database is made available to the table listener on the event object. The data object that is passed is an AgaRowData instance. The AgaRowData object is different from an AgaData object because it is used to access individual rows being modified by a table listener, not complete datasets.
When the rowDeleted or rowInserted events fire, you have access to the AgaRowData instance by calling the
When the rowModified event fires, you have access to the original version of the row using the
evt.getOldRowData() method, and the new values that the user entered using the e
The AgaRowData object that is returned by the
getOldRowData() contains the whole row before the user's modifications, and the AgaRowData object returned by the
getNewRowData() contains only the values for changed columns in the row.
The AgaRowData instance contains the following:
Note, however, that the AgaRowData might not include all of the columns for the row: For example, if you delete a row based on its key, the AgaRowData will contain only the key.
The AgaRowData class implements the AgiRowCursor interface, which lets you navigate a dataset. However, because this class contains only a single row at a time, it does not allow movement. If you call
gotoPrev(), or any of the other navigation methods, SilverStream will throw the AgoUnrecoverableSystemException. To obtain a value or set a value, use the
setProperty() methods, respectively.
NOTE SilverStream recommends that when you use either the
setProperty() method, you access database fields using the column name instead of the column number. If you use the column number, the operation will not fail, but SilverStream cannot guarantee that you will receive the expected result.
AgaRowData provides a method called
isPropertyAvailable(), which lets you test whether a specified property is in the current row. This method is especially useful with rowModify events when you call
evt.getNewRowData(), because the AgaRowData returned by this method contains only the columns that the user modified. You can use this method to determine the difference between a column that was not changed and a column that was changed to null.
isPropertyAvailable() can be called on all AgaRowDatas, it is useful only in the case of AgaRowDatas returned by
evt.getNewRowData(), which can contain some columns not filled in.
Table objects also support access to additional datasets. You can use the additional AgaData objects with the table listeners to perform other database tasks. These secondary AgaData objects have the full capabilities of any other AgaData object (including forward navigation). In other words, unlike the table listener's primary dataset, they have access to multiple rows of data. You access these additional datasets from the event object using the
getAgaData() method as described in
Triggered Object Basics.
NOTE You should only call
updateTransactionally() in the beforeTableChange, afterTableChange, or abort events. You might cause a database deadlock if you call them in the rowDeleted, rowModified, or rowInserted events.
Both the beforeTableChange and afterTableChange events receive an instance of the AgoTableEvent object. AgoTableEvent is the base class for all table event objects, such as AgoRowModified. It provides three important pieces of information that are not directly related to the data in the transaction, but are nonetheless important when working with table listeners.
The following table explains the three methods that return this information.
Returns the java.sql.Connection that this transaction is using. If your code is doing other updates to the database within this transaction, you have to use the same database connection to avoid a possible deadlock.
The integer has no relevance when compared to other transaction IDs (except for equality comparisons). You can use this transaction ID within the beforeTableChange, afterTableChange, rowDeleted, rowModified, rowInserted, and abort events to connect the operations of a single transaction in a server- lifetime business object.
The table listener operates within the context of a database transaction. As a result, table listeners can inspect the rows that are changing in a transaction. They can also change the data in the transaction. This is what happens when the SilverStream Server receives a database request on a table that has a table listener associated with it.
AgaData.updateRows()from a form, page, or other object).
NOTE A transaction might be rolled back by the database server at any time (for example, if concurrence problems occur when it tries to perform an insert, delete, or update operation). When the transaction is rolled back, the remaining object data events (rowModified, rowAdded, and so on) do not fire, nor does the afterTableChange event. When a transaction is rolled back (or aborted by a call to the
abortTransaction() method), SilverStream fires the aborted event.
You can use the
abortTransaction() method to determine whether a particular transaction is undone, rolled back, or committed. This table describes the
abortTransaction() method's effect, depending on the event in which you call it.
The transaction has not yet been started. Any database changes made up to this point are rolled back. The remainder of the transaction is discarded. The contents of the AgaData object are discarded. The aborted event fires. No other events fire.
The transaction has not yet been committed. Any changes made before
You can test the state of a transaction by calling the
isAborted() method. This method returns true if the transaction was aborted by the
abortTransaction() method. You cannot cancel the
abortTransaction() method operation.
If you call
abortTransaction(), any remaining row events and the afterTableChange event do not fire. Instead, the SilverStream Server fires the aborted event.
You can use table objects to inspect the row data as it is changed, or you can program the table object to change it. If you use table objects to modify the data during a transaction, keep the following in mind:
updateRows()on each AgaRowData object.
updateRows()are slightly different in an AgaRowData. Instead of "all changes so far will be in one transaction," it means "write these changes into the current transaction."
You can program table objects to respond to activity on tables that are versioned using the SilverStream version control feature. However, the standard insert, update, and delete database operations cause different events to fire in tables that are versioned.
When the SilverStream Server handles table versioning for you automatically, it updates both the primary (base) table and the version table.
The following diagram illustrates how the server handles different database operations.
As you can see, all of the database operations and their associated events are available to objects written on the base table, while objects written on the version table will fire on the rowAdded event, but never on the rowDeleted or rowModified events.
When you specify that versioned tables update manually, the SilverStream Server only logs updates in the version table. The base table is updated only when you publish the changes from the version table to the base table.
The following diagram illustrates the events that fire in the base table and the version table.
Objects on the base table can respond to the rowDeleted event and the rowModified event. The Version table only fires the rowAdded event.