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.
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>
In order to use database transactions, we first need to wire together a number of classes as follows:
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:
<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.
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);
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.
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.
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.
3.133.141.219