Menu
Simba Technologies
Simba Technologies

SimbaEngine X SDK 10.1.3
Developing Drivers for Data Stores Without SQL

Handling Transactions

A transaction is a set of operations that are executed on a data store. If a transaction is successful, all of the data modifications made during the transaction are committed. If a transaction encounters errors and must be canceled or rolled back, then all of the data modifications are erased.

If your data store supports transactions, you can enable them in your custom ODBC or JDBC driver. To enable transactions in your driver, your DSII must enable both read and write functionality.

To enable read/write capability on your driver:

  • For the C++ SDK, call DSIPropertyUtilities::SetReadOnly, passing in false for the second parameter.
  • For the Java SDK, call PropertyUtilities::SetReadOnly, passing in false for the second parameter.

Enabling Transaction Support

After adding read/write capability to your custom driver, you can enable transaction support by creating your own implementation of the DSIIConection class and implementing the BeginTransaction(), Commit(), and Rollback() methods.

Implement the DSIConnection Class

Support for transactions is implemented in the DSIConnection class. Override this class so you can provide your own implementation.

Specify that Transactions are Supported

The SimbaEngine X SDK uses a property to specify the level of transaction support for a custom driver. This is done differently in C++ and in Java.

To set the DSI_CONN_TXN_CAPABLE Property in C++:

Set the DSI_CONN_TXN_CAPABLE property in your DSIConnection object to specify the level of transaction support that your driver can handle. You can use the DSIPropertyUtilities::SetTransactionSupport() helper method.

Example: Setting the DSI_CONN_TXN_CAPABLE property in C++

In this example, a helper method called SetConnectionPropertyValues() is used to set the DSI_CONN_TXN_CAPABLE property. This method is invoked from the MyConnection class’s constructor. It calls the SetReadOnly method, passing in false:

void MyConnection::SetConnectionPropertyValues(){

  DSIPropertyUtilities::SetReadOnly(this, false);

  DSIPropertyUtilities::SetTransactionSupport(this, DSI_TC_DML);

...

In the above example, transaction support is set to DSI_TC_DML, which only supports DML statements within a transaction.

To set the DSI_TXN_CAPABLE Property in Java:

Set the ConnPropertyKey.DSI_TXN_CAPABLEin your DSIConnection object to specify the level of transaction support that your driver can handle. You can do this using the DSIConnection::SetProperty() method, passing DSI_TXN_CAPABLE as the attribute data.

Implement the Required Methods in the DSIConnection Class

To support transactions, your driver must implement the methods as described in this section.

  1. BeginTransaction()

    This method is invoked by the SimbaEngine X SDK at the start of a new transaction on the connection. This method is responsible for performing any logic that is required before the transaction starts, such as ensuring that transactions are supported, or checking that a transaction is not already in progress.

    Example: Check whether a transaction is already in progress

    In this example, the custom driver checks a member variable that tracks whether a transaction is already in progress. If so, an exception is thrown. Otherwise, the member is set to true at this point. Subsequent transaction methods will check this variable to coordinate their workflows with the current transaction.

    void MyConnection::BeginTransaction() {

        if (isInTransaction) {

            XMTHROWGEN(" Illegal transaction state change(BeginTransaction). ");

        }

        else {

            isInTransaction = true;

        }

    }

  2.  
  3. Commit()
  4. This method is invoked by the SimbaEngine X SDK to commit the statements of a transaction. This method is responsible for performing commit-related logic for the outstanding transaction on the connection, such as storing any inserted or updated data.

    Example: Sample Commit() Implementation

    This example shows one way that you could implement your Commit() method. It also shows a helper method, MyConnection::CommitImp().

    void MyConnection::Commit(){

    if (!isInTransaction) {

    XMTHROWGEN(" Illegal transaction state change(Commit). ");

    }

    else {

    isInTransaction = false;

    CommitImpl();

    }

    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////

    void MyConnection::CommitImpl() {

    // Get the Driver's Tables

    AutoValueMap<XMTableIdentifier, const XMTableData>& driverTableDataMap = GetTableDataMap();

    CriticalSectionLock lock(m_criticalSection); 

    for (AutoValueMap<XMTableIdentifier, const XMTableData>::iterator it = m_changedTables.begin(); it != m_changedTables.end(); it++){

    MapUtilities::InsertOrUpdate(driverTableDataMap, it->first, it->second);

    it->second = NULL;

    }

    }

    MyConnection::Commit() first ensures that a transaction is in progress, and then delegates the commit logic to MyConnection::CommitImpl. MyConnection::CommitImpl first takes a lock. Then, it iterates through its m_changedTables member, which is used to track tables that have had inserts or updates to made to their values. Finally, it uses the MapUtilities::InsertOrUpdate helper method to perform the actual data value insertion/updates on these tables.

     

  5. Rollback()
  6. This method is invoked by the SimbaEngine X SDK when a ROLLBACK statement is encountered in a transaction query. This method is responsible for rolling back data for an outstanding transaction on the connection, to the state it was in before the start of the transaction.

    Example: Sample Rollback() Implementation

    This example shows one way that you could implement your Rollback() method.

    void XMConnection::Rollback(){

    if (!isInTransaction){

    XMTHROWGEN(" Illegal transaction state change(Rollback). ");

    }

    else {

    isInTransaction = false;

    m_changedTables.DeleteClear();

    }

    }

    This method first ensures that a transaction is in progress. If the transaction is in progress, it resets isInTransaction to indicate that a transaction is no longer in progress, followed by a call to m_changedTables.DeleteClear(), which clears the listing of tables that have been modified.

Adding Support for Savepoints (SimbaJDBC only)

A savepoint is a way of implementing subtransactions, which are also called nested transactions. A savepoint is used to mark a point in a transaction that you can roll back to without affecting any work done in the transaction before the savepoint was created. Savepoints are supported for DSIIs that are written in Java and use the SimbaJDBC component.

Set the DSI_SUPPORTS_SAVEPOINTS property

Set the DSI_SUPPORTS_SAVEPOINTS property in your custom DSIConnection object to DSI_SUPPORTS_SAVEPOINTS_TRUE.

Implement the Required Methods in the DSIConnection Class

Modify your custom DSIConnection object to override and implement the following virtual methods:

  1. createSavepoint(String)
  2. This method is invoked by the SimbaEngine SDK when a SAVEPOINT statement is encountered in a query. This method is responsible for creating a new Savepoint with the specified name in the current transaction, and performing any save point logic such as caching information about the current state of data, that could be used to restore the state if a subsequent rollback operation occurs.

  3. releaseSavepoint(String)
  4. This method is invoked by the SimbaEngine SDK when a RELEASE statement is countered in a query. This method is responsible for releasing the Savepoint with the specified name so that the Savepoint is no longer available to rollback to. This could also include performing any related logic such as freeing up resources and clearing any state information that was related to the specified save point.

  5. rollback(String)
  6. This method is invoked by the SimbaEngine SDK when a ROLLBACK statement is encountered in a transaction query. This method is responsible for rolling back data for an outstanding transaction on the connection, to the state it was in before the start of the transaction.

Supporting Transactions through SQL

In some data sources, transactions can also be triggered by executing certain SQL queries (e.g. BEGIN, COMMIT, AND ROLLBACK statements). Support for this is provided through the ITransactionStateListener interface, which allows your DSII to inform the Simba components of any changes in transaction state.

In your CustomerDSIConnection object, invoke the following methods on the m_transactionStateListener member exposed by the DSIConnection class. This informs the Simba components of any changes to transaction state.

In the C++ SDK:

  1. When a transaction has started, call ITransactionStateListener::NotifyBegin.
  2. When a transaction is committed, call ITransactionStateListener::NotifyCommit.
  3. When a transaction is rolled back, call ITransactionStateListener::NotifyRollback.

In the Java SDK:

  1. When a transaction has started, call ITransactionStateListener.NotifyBeginTransaction.
  2. When a transaction is committed, call ITransactionStateListener::NotifyCommit.
  3. When a transaction is rolled back, call ITransactionStateListener::NotifyRollback.

If your DSII is written in Java and is using the SimbaJDBC component, then you may need to notify the ITransactionStateListener about Savepoint operations as well:

  1. When a Savepoint is created, call ITransactionStateListener::notifyCreateSavepoint.
  2. When a Savepoint is released, call ITransactionStateListener::notifyReleaseSavepoint.
  3. When a transaction is rolled back to a Savepoint, call ITransactionStateListener::notifyRollbackSavepoint.

Important:

ODBC does not support Savepoints, and attempting to use the notify*Savepointfunctions on the ITransactionStateListener while using the JNI DSI API will cause an exception.