Skip Headers
Oracle® TimesTen In-Memory Database TTClasses Guide
Release 11.2.1

Part Number E13074-02
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Master Index
Master Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

3 Using TTClasses

This chapter contains brief descriptions of the recommended way to use TTClasses. It includes the following topics:

Using TTCmd, TTConnection, and TTStatus

While TTClasses can be used in a number of ways, the following general approach has been successfully and can easily be adapted to a variety of applications.

To achieve optimal performance, real-time applications should use prepared SQL statements. Ideally, all SQL statements that will be used by an application are prepared when the application begins, using separate TTCmd objects for each statement. In ODBC (and thus in the C++ classes), statements are bound to a particular connection, so a full set of all statements used by the application will often be associated with every connection to the TimesTen database.

An easy way to accomplish this is to develop an application-specific class that is derived from TTConnection. For an application called XYZ, you can create a class called XYZConnection, derived from TTConnection. The XYZConnection class contains private TTCmd members representing the prepared SQL statements that can be used in the application. In addition, the XYZConnection class provides new public methods to implement the application-specific database functionality, which can be implemented using the private TTCmd members.

Example 3-1 Definition of a Connection Class

This is an example of a class that inherits its functionality from TTConnection.

class XYZConnection : public TTConnection {
private:
  TTCmd updateData;
  TTCmd insertData;
  TTCmd queryData;

public:
  XYZConnection();
  ~XYZConnection();
  virtual void Connect (const char* connStr,TTStatus&);
  void updateUser (TTStatus&);
  void addUser (char* nameP, TTStatus&);
  void queryUser (const char* nameP, int* valueP,
                  TTStatus&);
};

In the preceding example, an XYZConnection object is a connection to TimesTen that can be used to perform three application-specific operations: addUser(), updateUser(), and queryUser(). These operations are specific to the application (storing account balances, for example). The implementation of these three methods can use updateData(), insertData(), and queryData() provided in TTCmd to implement the specific functionality of the application.

To cause the SQL statements used by the application to be prepared, the XYZConnection class overloads the Connect() method provided by the TTConnection base class. The XYZConnection::Connect() method will call the Connect() method of the base class to establish the database connection, and also calls the Prepare() method for each TTCmd object to cause the SQL statements to be prepared for later use.

Example 3-2 Definition of a Connect() method

This example shows the XYZConnection::Connect() method.

void
XYZConnection::Connect(const char* connStr, TTStatus&
                       stat)
{
  TTStatus stat2;

  try {
    TTConnection::Connect(connStr, stat);
    updateData.Prepare(this,
                       "update mydata v
                       "set foo = ? where bar = ?",
                       stat);
    insertData.Prepare(this, 
                       "insert into mydata "
                       "values(?,0)", stat);
    queryData.Prepare(this,
                     "select i from mydata where name "
                     " = ?", stat);
    Commit(stat);
  }
  catch (TTStatus st) {
    cerr << "Error in XYZConnection::Connect: " << st
         << endl;
    Rollback(stat2);
  }
  return;
}

This Connect() method causes the XYZConnection to be made fully operational. The application-specific methods are fully functional after Connect() has been called.

This approach to application design works well with the design of the TTConnectionPool class. The application can create numerous objects of type XYZConnection and can add them to TTConnectionPool. By calling TTConnectionPool::ConnectAll(), the application can cause all connections in the pool to be connected to the database, as well as causing all SQL statements to be prepared, in a single line of code.

This approach to application design allows the database components of an application to be separated from the remainder of the application; only the XYZConnection class contains database-specific code.

An example of this type of design can be found in several of the sample programs that are included with TTClasses. The simplest example is sample.cpp.

Note that other configurations are possible. Some customers have extended this scheme further, so that SQL statements to be used in an application are listed in a table in the database, rather than being hard-coded in the application itself. This allows changes to database functionality to be implemented by making database changes rather than application changes.

TTClasses logging

TTClasses has a logging facility that allows applications to capture useful debugging information about running TTClasses programs. TTClasses logging is done on at the process level. You can enable logging for a specific process and produce a single output log stream for the process. TTClasses logging is disabled by default.

TTClasses supports different levels of logging information. See Example 3-4 for more information about what is printed at each log level.

Log level WARN is very useful while developing a TTClasses application and can also be appropriate for production applications because in this log level database query plans are generated.

Note that at the more verbose log levels (INFO and DEBUG), so much log data is generated that application performance is adversely affected. We strongly discourage using these log levels in a production environment.

Although TTClasses logging can print to either stdout or stderr, the best approach is to write directly to a TTClasses log file. Example 3-3 demonstrates how to print TTClasses log information at log level WARN into the /tmp/ttclasses.log output file.

Example 3-3 Printing TTClasses log information

ofstream output;
output.open("/tmp/ttclasses.log");
TTGlobal::setLogStream(output);
TTGlobal::setLogLevel(TTLog::TTLOG_WARN);

First-time users of TTClasses should spend a little time experimenting with TTClasses logging. You can change the sample.cpp program to use different log levels so you can see how errors are printed at log level ERROR and how huge amounts of logs are generated at log levels INFO and DEBUG.

See "TTGlobal" for more information about using the TTGlobal class for logging.

TTClasses XLA classes

The Transaction Log API (XLA) is a set of functions that enable you to implement applications that:

One of the purposes of XLA is to provide a high-performance, asynchronous alternative to triggers.

For additional information about XLA, see the chapter "XLA and TimesTen Event Management" in the Oracle TimesTen In-Memory Database C Developer's Guide.

TTClasses XLA demos

TimesTen provides TTClasses XLA demos in the following location:

  • install_dir/quickstart/sample_code/ttclasses/xla

Refer to "About the TimesTen TTClasses demos".

The README file in the ttclasses directory contains instructions for building and running the TTClasses XLA demos (as well as other TTClasses demos).

Acknowledging XLA updates at transaction boundaries

XLA returns notification of changes to specific tables in the database, as well as information about the transaction boundaries for those database changes. This section shows how to acknowledge updates only at transaction boundaries (a common requirement for XLA applications), using one example that does not use and one example that does use transaction boundaries.

Example 3-4 TTClasses XLA program

This example shows a typical main loop of a TTClasses XLA program.

TTXlaPersistConnection conn; // XLA connection
TTXlaTableList list(&conn); // tables being monitored
ttXlaUpdateDesc_t ** arry; // ptr to returned XLA recs
TTStatus stat;
int records_fetched;
// ...

loop {
  // fetch the updates
  conn.fetchUpdatesWait(&arry, MAX_RECS_TO_FETCH,
                               &records_fetched, ...); 

  // Interpret the updates
  for(j=0;j < records_fetched;j++){
    ttXlaUpdateDesc_t *p;
    p = arry[j];
    list.HandleChange(p, NULL);
  } // end for each record fetched

  // periodically call ackUpdates()
  if (some condition is reached) {
    conn.ackUpdates(stat) ;      
  }
} // loop

Inside the HandleChange() method, depending on whether the record is an insert, update, or delete, the appropriate method of the following is called: HandleInsert(), HandleUpdate(), or HandleDelete().

It is inside HandleChange() that you can access the flag that indicates whether the XLA record is the last record in a particular transaction.

Thus there is no way in the example loop for the HandleChange() method to pass the information about the transaction boundary to the loop, so that this information can influence when to call conn.ackUpdates().

Under typical circumstances of only a few records per transaction, this is not an issue. When you ask XLA to return at most 1000 records with the fetchUpdatesWait() method, usually only a few records are returned. XLA returns records as quickly as it can, and even if huge numbers of transactions are occurring in the database, you usually can pull the XLA records out quickly, a few at a time. When you pull the XLA records out a few at a time, XLA usually makes sure that the last record returned is on a transaction boundary.

In summary: if you ask for 1000 records from XLA, and XLA returns only 15, it is highly probable that the 15th record is at the end of a transaction.

XLA guarantees one of the following:

  • Either a batch of records will end with a completed transaction (perhaps multiple transactions in a single batch of XLA records)

  • Or a batch of records will contain a partial transaction, with no completed transactions in the same batch, and that subsequent batches of XLA records will be returned for that single transaction until its transaction boundary has been reached.

Careful XLA applications need to verify whether the last record in a batch of XLA records has a transaction boundary and call ackUpdates() only on this transaction boundary. This is especially important when operations involve a large number of rows. If a bulk insert/update/delete operation has been performed on the database and the XLA application asks for 1000 records, it might receive all 1000 records (or fewer than 1000). The last record returned through XLA will probably not have the end-of-transaction flag. In fact, if the transaction has made changes to 10,000 records, then clearly a minimum of 10 blocks of 1000 XLA records must be fetched before reaching the transaction boundary.

Calling ackUpdates() for every transaction boundary is not recommended, because ackUpdates() is a relatively expensive operation. Careful XLA applications should make sure to call this method only on a transaction boundary, so that when the application or system or database fails, the XLA bookmark is at the start of a transaction after the system recovers.

The HandleChange() method has a second parameter to allow passing information between HandleChange() and the main XLA loop. Compare Example 3-4 with Example 3-5 (the do_acknowledge setting, and the &do_acknowledge parameter of the HandleChange() call).

Example 3-5 TTClasses XLA program using transaction boundaries

In this example, ackUpdates() is called only when the do_acknowledge flag indicates that this batch of XLA records is at a transaction boundary.

TTXlaPersistConnection conn; // XLA connection
TTXlaTableList list(&conn); // tables being monitored
ttXlaUpdateDesc_t ** arry; // ptr to returned XLA recs
TTStatus stat;
int records_fetched;
int do_acknowledge;
// ...
loop {
  // fetch the updates
  conn.fetchUpdatesWait(&arry, MAX_RECS_TO_FETCH,
                               &records_fetched, ...); 

  do_acknowledge = FALSE;

  // Interpret the updates
  for(j=0;j < records_fetched;j++){
    ttXlaUpdateDesc_t *p;
    p = arry[j];
    list.HandleChange(p, &do_acknowledge);
  } // end for each record fetched

  // periodically call ackUpdates()
  if (do_acknowledge == TRUE 
/* and some other conditions ... */ ) {
    conn.ackUpdates(stat) ;      
  }
} // loop

In addition to this change to the XLA main loop, the HandleChange() method needs to be overwritten to use ttXlaUpdateDesc_t.