Using transactions with a database

This recipe will show you how to set up transaction management over a database used from a Camel route. It will also detail the transaction propagation behaviors that you can use to combine both transactional and non-transactional database interactions.

Getting ready

The Java code for this recipe is located in the org.camelcookbook.transactions.databasetransaction package. The Spring XML files are located under src/main/resources/META-INF/spring and prefixed with databaseTransaction.

As discussed in the Introduction section, Camel's transaction handling relies on Spring's PlatformTransactionManager abstraction. Therefore, it is necessary to include the appropriate dependency in your application regardless of whether you are basing it on Spring or not—this includes regular Java as well as OSGi Blueprint applications.

To use Spring-managed JDBC transactions, you will need the following Maven dependencies:

<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-spring</artifactId>
  <version>${camel-version}</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>${spring-version}</version>
</dependency>

How to do it...

In order to use database transactions, we first need to wire together a number of classes as follows:

How to do it...

The javax.sql.DataSource interface is the main entry point to your database. The actual implementation class used here may come directly from your database vendor, or may be a database connection pool, such as c3p0 (http://www.mchange.com/projects/c3p0/).

In this example, a DataSource is instantiated to point to a database used for audit records. This instance is referred to as the auditDataSource.

The steps required to set up transactional access to a database are as follows:

  1. Wire the classes together in Spring.
    <bean id="sql"
          class="org.apache.camel.component.sql.SqlComponent">
      <property name="dataSource"
                ref="auditDataSource"/>
    </bean>
    
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource"
                ref="auditDataSource"/>
    </bean>
    
    <bean id="PROPAGATION_REQUIRED"
          class="org.apache.camel.spring.spi.SpringTransactionPolicy">
      <property name="transactionManager"
                ref="transactionManager"/>
      <property name="propagationBehaviorName" 
                value="PROPAGATION_REQUIRED"/>
    </bean>

    The SpringTransactionPolicy instance defines a transaction propagation behavior, which is interpreted by the Spring framework. A propagationBehaviorName value of PROPAGATION_REQUIRED indicates that a transaction should be started if one is not already in progress, otherwise the transaction in progress should be used.

    Note

    By convention, transaction policies are typically given IDs that are the same as the value of their propagationBehaviorName. This convention is used by Camel to provide a sensible default configuration for transactions when the beans are not explicitly wired together.

    In Java, the same objects can be wired into a standalone Camel context as follows:

    SimpleRegistry registry = new SimpleRegistry();
    DataSource auditDataSource = ...; // defined by you 
    
    DataSourceTransactionManager transactionManager = 
        new DataSourceTransactionManager(auditDataSource);
    registry.put("transactionManager", transactionManager);
    
    SpringTransactionPolicy propagationRequired = 
        new SpringTransactionPolicy();
    propagationRequired.setTransactionManager(
        transactionManager);
    propagationRequired.setPropagationBehaviorName(
        "PROPAGATION_REQUIRED");
    registry.put("PROPAGATION_REQUIRED", propagationRequired);
    
    CamelContext camelContext =
        new DefaultCamelContext(registry);
    SqlComponent sqlComponent = new SqlComponent();
    sqlComponent.setDataSource(auditDataSource);
    camelContext.addComponent("sql", sqlComponent);

    Note

    In Java, both the PlatformTransactionManager and SpringTransactionPolicy must be registered in Camel's object registry explicitly, as the transaction mechanism needs to obtain a reference to them. When using Spring, any bean defined in the ApplicationContext is visible to Camel.

  2. To access the database transaction manner in Camel, define a transacted block before accessing the SQL Component:
    <from uri="direct:transacted"/>
    <transacted/> 
    <setHeader headerName="message">
      <simple>${body}</simple>
    </setHeader>
    <to uri="sql:insert into audit_log (message) values(:#message)"/>
    <to uri="mock:out"/>

    The transacted block will start a transaction that is committed at the end of the route.

    In the Java DSL, the same route can be represented as:

    from("direct:transacted")
      .transacted()
      .setHeader("message", body())
      .to("sql:insert into audit_log (message) values(:#message)")
      .to("mock:out");

Both of the preceding routes will insert the body of a message into the audit_log table. If the mock:out endpoint throws an exception, the database insert will be rolled back.

How it works...

The transacted statement uses conventions to provide sensible transaction behavior. Its processor will fetch a SpringTransactionPolicy named PROPAGATION_REQUIRED from Camel's registry. If none is defined, it will fetch a PlatformTransactionManager object and implicitly use the PROGAGATION_REQUIRED behavior.

In the preceding code, the transacted statement will start a transaction using the DataSourceTransactionManager instance. Under the covers, this means it will fetch a database connection from the DataSource and set it to manual commit. Any interaction with the DataSource object for the remainder of the route by this thread will be through this transaction.

The processor will then set up Camel's TransactionErrorHandler to roll back transactions when an exception is thrown and not handled by another error handler.

If the exchange is completed without any problems, the transaction will commit at the end of the route. If an exception is thrown, all database changes made will be rolled back.

There's more...

If you have multiple PlatformTransactionManager or SpringTransactionPolicy objects defined, or if you just want to be explicit about your configuration, you can define exactly which SpringTransactionPolicy object from the registry is to be used by the transacted statement.

In the XML DSL, this is written as:

<transacted ref="PROPAGATION_REQUIRED"/>

In the Java DSL, the same thing is expressed as:

.transacted("PROPAGATION_REQUIRED")

The following table describes the various Spring-supported transaction propagation behaviors that may be used with the transacted DSL statement.

PROPAGATION_REQUIRED

Start a transaction if none is already in progress; otherwise join the existing one. This is the most commonly used option.

PROPAGATION_REQUIRES_NEW

Start a transaction regardless of whether another transaction is already in progress. If a transaction is in progress, it will be suspended, and resumed when this one completes.

PROPAGATION_MANDATORY

A transaction must be in progress by the time this block is reached, if not, an exception will be thrown.

PROPAGATION_SUPPORTS

If a transaction is in progress, it will be used. Otherwise the logic will be non-transactional.

PROPAGATION_NOT_SUPPORTED

If a transaction is in progress when this block is reached, it will be suspended. The logic within will be executed outside of a transaction.

PROPAGATION_NEVER

A transaction must not be in progress when this block is reached, otherwise an exception will be thrown.

PROPAGATION_NESTED

Starts a new transaction whose outcome is tied to any transaction that was already in progress.

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.141.219