Berkeley DB Reference Guide:
Java API Tutorial - Basic

PrevRefNext

Using transactions

Berkeley DB Transactional Data Store applications have standard transactional characteristics: recoverability, atomicity and integrity. The Java API provides these transactional capabilities using a transaction-per-thread model. Once a transaction is begun, it is implicitly associated with the current thread until it is committed or aborted. This model is used for the following reasons.

The Java API provides two transaction APIs. The lower-level API is the CurrentTransaction class. It provides a way to get the transaction for the current thread, and to begin, commit and abort transactions. It also provides access to the Berkeley DB core API DbTxn object. With CurrentTransaction , just as in the core API, the application is responsible for beginning, committing and aborting transactions, and for handling deadlock exceptions and retrying operations. This API may be needed for some applications, but it is not used in the example.

The example uses the higher-level TransactionRunner and TransactionWorker APIs, which are build on top of CurrentTransaction . TransactionRunner.run automatically begins a transaction and then calls the TransactionWorker.doWork method, which is implemented by the application.

The TransactionRunner.run method automatically detects deadlock exceptions and performs retries by repeatedly calling the TransactionWorker.doWork method until the operation succeeds or the maximum retry count is reached. If the maximum retry count is reached or if another exception (other than DbDeadlockException ) is thrown by TransactionWorker.doWork , then the transaction will be automatically aborted. Otherwise, the transaction will be automatically committed.

Using this high-level API, if TransactionRunner.run throws an exception, the application can assume that the operation failed and the transaction was aborted; otherwise, when an exception is not thrown, the application can assume the operation succeeded and the transaction was committed.


The Sample.run method creates a TransactionRunner object and calls its TransactionRunner.run method.

import com.sleepycat.bdb.TransactionRunner;
import com.sleepycat.bdb.TransactionWorker;
...
public class Sample
{
    private SampleDatabase db;
    ...
    private void run()
        throws Exception
    {
        TransactionRunner runner = new TransactionRunner(db.getEnvironment());
        runner.run(new PopulateDatabase());
        runner.run(new PrintDatabase());
    }

private class PopulateDatabase implements TransactionWorker { public void doWork() throws Exception { } }

private class PrintDatabase implements TransactionWorker { public void doWork() throws Exception { } } }

The run method is called by main and was outlined in the previous section. It first creates a TransactionRunner , passing the database environment to its constructor.

It then calls TransactionRunner.run to execute two transactions, passing instances of the application-defined PopulateDatabase and PrintDatabase inner classes. These classes implement the TransactionWorker.doWork method and will be fully described in the next two sections.

For each call to TransactionRunner.run , a separate transaction will be performed. The use of two transactions in the example -- one for populating the database and another for printing its contents -- is arbitrary. A real-life application should be designed to create transactions for each group of operations that should have recoverability, atomicity and integrity, while also taking into account the impact of transactions on performance.

The advantage of using TransactionRunner is that deadlock retries and transaction begin, commit and abort are handled automatically. However, a TransactionWorker class must be implemented for each type of transaction. If desired, anonymous inner classes can be used to implement the TransactionWorker interface.


PrevRefNext

Copyright (c) 1996-2003 Sleepycat Software, Inc. - All rights reserved.