Programmer's Guide



Chapter 28   Using Table-Modified Business Objects

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:

About table-modified business objects   Top of page

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).

Uses of table listeners   Top of page

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 and external table modifiers   Top of page

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.

Creating a table listener   Top of page

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.

Importing a table listener created externally

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.

How table listeners work   Top of page

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.

Component

Description

SilverStream Application Server

Provides the context for the table object.

Table Listener

The object that programmatically responds to changes to a specific database table.

Table Event Object

Objects passed from the server to the table listener that provide method access to other information on the server.

AgaData

A helper object that allows you to access the table data. This object is accessible from all triggered business objects.

AgaRowData

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.

Its contents depend on the event object on which it is passed.

Table listener events   Top of page

A different event is fired for each database operation that occurs. This table describes the events that are fired for table listeners.

Event

Description

beforeTableChange

Fires just before the application server starts a database transaction.

Receives an AgoTableEvent object.

rowDeleted

Fires just before the application server deletes a database row.

Receives an AgoRowDeleteEvent object.

rowInserted

Fires just before the application server inserts a database row.

Receives an AgoRowInsertEventobject.

rowModified

Fires just before the application server modifies a database row.

Receives an AgoRowModifiedEvent object.

afterTableChange

Fires just after the application server commits the database request.

Receives an AgoTableEvent Object.

abort

Fires if the transaction is aborted by any means and for any reason. When abort fires, the afterTableChange event does not fire.

Receives an AgoTransactionAbortEvent object.

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 event objects   Top of page

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.

Getting data from event objects   Top of page

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 evt.getRowData() method.

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 evt.getNewRowData() method.

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.

About the AgaRowData object   Top of page

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 gotoNext(), gotoPrev(), or any of the other navigation methods, SilverStream will throw the AgoUnrecoverableSystemException. To obtain a value or set a value, use the getProperty() and setProperty() methods, respectively.

NOTE   SilverStream recommends that when you use either the getProperty() or 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.

Accessing modified row data

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.

Although 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.

Accessing additional datasets   Top of page

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 updateRows() or 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.

Getting other information from event objects   Top of page

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.

Method

Description

getConnection()

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.

getTableName()

Returns the name of the table on which the database operation is occurring.

getTransactionID()

Returns an integer that identifies the transaction of this event.

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.

Table listeners and database transactions   Top of page

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.

  1. The SilverStream Server receives a request to update the database (usually through an AgcData.updateRows(), AgpData.updateRows(), or AgaData.updateRows() from a form, page, or other object).

  2. SilverStream checks whether there are table listeners for any of the database tables associated with the update request. If there are, then the SilverStream Server instantiates them (as necessary) and fires the beforeTableChange event for all of the listeners for that database table.

  3. The database server starts a transaction.

  4. The SilverStream Server submits insert, delete, and update requests in the order in which they occurred.

  5. When all of the transactions are submitted, the database server commits the transaction, then the SilverStream Server fires the afterTableChange event.

  6. Once the afterTableChange event completes, SilverStream makes any event-lifetime table objects available for garbage collection.

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.

Aborting a transaction   Top of page

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.

Event

Result

beforeTableChange

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.

rowAdded

rowModified

rowDeleted

The transaction has not yet been committed. Any changes made before abortTransaction() are undone. The remainder of the transaction is discarded. The contents of AgaData are discarded. The aborted event fires. No other events fire, including afterTableChange.

afterTableChange

The transaction has been committed. You cannot call abortTransaction() here.

Testing the transaction state

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.

Using table listeners to modify the database   Top of page

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:

Working with versioned tables   Top of page

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.

Working with automatically versioned tables   Top of page

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.

Working with manually versioned tables   Top of page

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.






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