Chapter 7. Transactions

Table of Contents

Enabling and Starting Transactions
Committing and Aborting Transactions
Aborting Transactions
Using Autocommit
Transactional Cursors
Configuring Dirty Reads
Transactions and Concurrency
Transactions and Deadlocks
Performance Considerations
Transactions Example

Transactions cause one or more database operations to be treated as a single unit of work. Either all of the operations succeed, or all of them fail. To use transactions, you specify when a transaction begins and ends, and you specify what operations are performed within the transaction. You also define when a transaction should abort (fail) in your error handling code.

JE offers full ACID coverage through its transactions. That is, JE's transactions offer:

In general you should use transactions whenever you are performing write operations. However, transaction usage does result in a performance penalty. Applications that are IO-bound might want to avoid them, especially if your databases are easily recreated such as what might occur if you are using JE as a non-persistent caching mechanism.

Enabling and Starting Transactions

Before you can transactionally protect your database modifications, you must:

  1. Enable transactions for your Environment. You do this using the EnvironmentConfig.setTransactional() method, or through the je.env.isTransactional je.properties parameter.

  2. Enable transactions for your Database. You do this using the DatabaseConfig.setTransactional() method.

  3. Open your Database from within a transaction. For best results, you should commit the transaction used to open your database as soon as the open operation completes. Using autocommit is an excellent way of ensuring that this happens (see below).

Once you have enabled transactions for a given environment and database, then all database modifications performed for that Database handle must be transactionally protected. Similarly, if you open an environment or database without enabling transactions, then you can not use transactions to protect modifications performed for that Environment or Databasehandle. Finally, read operations never require transactional protection regardless of whether transactions are enabled for the environment. However, remember that you do suffer at least a small performance penalty when using transactions. If possible, you should avoid transactionally protecting read-only operations.

You start a transaction using the Environment.beginTransaction() method. You can commit a transaction using the Transaction.commit() method.

For example:

import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.Transaction;

...

try {
    EnvironmentConfig myEnvConfig = new EnvironmentConfig();
    DatabaseConfig myDbConfig = new DatabaseConfig();
    myEnvConfig.setTransactional(true);
    myDbConfig.setTransactional(true);

    Environment myEnv = new Environment(new File("/my/env/home"), 
                                        myEnvConfig);

    Transaction txn = myEnv.beginTransaction(null, null);
    Database myDb = myEnv.openDatabase(txn, "myDbName", myDbConfig);
    txn.commit(); 
} catch (DatabaseException de) {
    // Exception handling goes here
}