Getting started with the Transaction Control Service

To make use of scoped resources and transactions using the transaction control service you need two things:

  • A org.osgi.service.transaction.control.TransactionControl implementation (found in the service registry). You may want to start with local transactions.

  • A org.osgi.service.transaction.control.ResourceProvider for each of the resources that you want to use. You may want to start with local JDBC.

Scoping Work using TransactionControl

The Transaction Control Service defines three different scopes:

  • Unscoped - There is no scope associated with the current thread

  • No Transaction Scope - There is a scope associated with the current thread, but no ongoing transaction

  • Transactional Scope - There is an ongoing transaction associated with the current thread

Scoped resources have different behaviours in each of these three scopes:

  • Unscoped - The resource is generally not usable and will throw exceptions

  • No Transaction Scope - The same physical resource will be used throughout the scope, and will be automatically tidied up at the end of the scope (e.g. closed or returned to a pool)

  • Transactional Scope - The same physical resource will be used throughout the scope, will be automatically committed or rolled back up at the end of the transaction, and then tidied up afterwards

Starting and Finishing scopes

A scope is defined using a piece of work wrapped in a Callable. This means that it is lambda-friendly.

Integer result = txControl.required(() -> {
        //Work goes in here
        return 42;
    });

The scope starts immediately before the work is executed, and finishes immediately afterwards. The required and requiresNew methods can be used to ensure that a Transactional scope has been started. The supports and notSupported methods can be used to ensure that a No Transaction scope has been started.

Simple scope management is perfect in most situations, but you may also wish to read about more advanced scope control techniques or exception management once you’ve mastered the basics. There are also some things to consider if you’re migrating from Spring or Java EE.

Accessing Resources

A ResourceProvider is a generic factory for scoped resources. Typically you will use a more specific interface for type safety. For example the Transaction Control specification defines JDBCConnectionProvider and JPAEntityManagerProvider interfaces. If needed you can make your own ResourceProvider.

To create your scoped resource you make one call to getResource passing in the TransactionControl service that the resource should integrate with. The returned object is thread-safe, and can be cached for use in any scope.

Declarative Services Example

The following component provides read and write access using JDBC to a list of messages created by a user. The transactionality and lifecycle of the database resources is automatically managed.

@Component
public class MyDaoImpl implements MyDao {

    @Reference
    TransactionControl control;

    Connection dbConn;

    @Reference
    void setResource(JDBCConnectionProvder provider) {
        dbConn = provider.getResource(control);
    }

    @Override
    public void saveMessage(String user, String message) {
        txControl.required(() -> {
                PreparedStatement ps = connection.prepareStatement(
                        "Insert into MESSAGES values ( ?, ? )");
                ps.setString(1, user);
                ps.setString(2, message);
                return ps.executeUpdate();
            });
    }

    @Override
    public void getMessagesForUser(String user) {
        return txControl.supports(() -> {
                PreparedStatement ps = connection.prepareStatement(
                        "Select MESSAGE FROM MESSAGES WHERE USER = ?");
                ps.setString(1, user);

                List<String> result = new ArrayList<>();

                ResultSet rs = ps.executeQuery();

                while(rs.next()) {
                    result.add(rs.getString(1));
                }

                return result;
            });
    }
}