The technique shown in the Using transactions with a database recipe used the transacted
DSL statement to initiate a transaction. Using this approach, the transaction is committed when the exchange's processing completes. This recipe will show you how you can control the scope of transactions in a much more granular fashion using the policy
statement.
The Java code for this recipe is located in the org.camelcookbook.transactions.transactionpolicies
package. The Spring XML files are located under src/main/resources/META-INF/spring
and prefixed with transactionPolicies
.
Set up a SpringTransactionPolicy
associated with a transaction manager as shown in the Using transactions with a database recipe.
Using the policy
DSL statement, wrap the individual processing steps that you want to enclose in a transaction, referring to the SpringTransactionPolicy
by name (in this example, we refer to a bean with the id "PROPAGATION_REQUIRED
").
In the XML DSL, this is expressed as follows:
<from uri="direct:policies"/> <setHeader headerName="message"> <simple>${body}</simple> </setHeader> <policy ref="PROPAGATION_REQUIRED"> <!-- Transaction 1 --> <to uri="sql:insert into audit_log (message) values(:#message)"/> <to uri="mock:out1"/> </policy> <policy ref="PROPAGATION_REQUIRED"> <!-- Transaction 2 --> <to uri="sql:insert into messages (message) values(:#message)"/> <to uri="mock:out2"/> </policy>
In the Java DSL, the same routing logic can be expressed as:
from("direct:policies") .setHeader("message", body()) .policy("PROPAGATION_REQUIRED") // Transaction 1 .to("sql:insert into audit_log (message) values (:#message)") .to("mock:out1") .end() .policy("PROPAGATION_REQUIRED") // Transaction 2 .to("sql:insert into messages (message) values (:#message)") .to("mock:out2") .end();
In the preceding example, the route defines two separate transactions. When the end of the first policy
block is reached, Transaction 1
will be committed. When the exchange reaches the second policy
block, a new transaction will be started. If an exception is thrown by the mock:out2
endpoint, only Transaction 2
will be rolled back.
The policy
block defines a boundary around a series of processing steps with a "before" and "after" processing step being performed by an implementation of the org.apache.camel.spi.Policy
interface. SpringTransactionPolicy
implements this interface.
When referring to a SpringTransactionPolicy
instance, a transaction will be initialized according to the propagation behavior that you have specified (see the Using transactions with a database recipe for an outline of the available options). When the policy
block completes, the transaction will be completed as per the logic defined by that propagation behavior.
The policy
block is a general-purpose mechanism for scoping "before" and "after" behavior. Aside from transactions, it is also used by the Camel Shiro and Camel Spring Security Components (see Authentication and authorization using Spring Security in Chapter 11, Security) to allow you to define security constrains around processing steps.
Transactions may be nested using this mechanism by referring to different SpringTransactionPolicy
objects. In the following example, we embed a non-transacted SQL insert into an audit_log
table inside a wider transaction. An exception thrown by the mock:out1
endpoint would remove the entry from the messages
table, but not the audit_log
table.
In the XML DSL, this is written as:
<route> <from uri="direct:policies"/> <setHeader headerName="message"> <simple>${body}</simple> </setHeader> <policy ref="PROPAGATION_REQUIRED"> <to uri="sql:insert into messages (message) values(:#message)"/> <to uri="direct:nestedPolicy"/> <to uri="mock:out1"/> </policy> </route> <route> <from uri="direct:nestedPolicy"/> <policy ref="PROPAGATION_NOT_SUPPORTED"> <to uri="sql:insert into audit_log (message) values(:#message)"/> <to uri="mock:out2"/> </policy> </route>
In the Java DSL, the same routing logic is expressed as:
from("direct:policies") .setHeader("message", simple("${body}")) .policy("PROPAGATION_REQUIRED") .to("sql:insert into messages (message) values (:#message)") .to("direct:nestedPolicy") .to("mock:out1") .end(); from("direct:nestedPolicy") .policy("PROPAGATION_NOT_SUPPORTED") .to("sql:insert into audit_log (message) values (:#message)") .to("mock:out2") .end();
If nesting two transactions with the PROPAGATION_REQUIRES_NEW
behavior, both policy
blocks should refer to different SpringTransactionPolicy
objects with the same behavior otherwise the second policy
will have no impact:
<bean id="PROPAGATION_REQUIRES_NEW" class="org.apache.camel.spring.spi.SpringTransactionPolicy"> <property name="transactionManager" ref="transactionManager"/> <property name="propagationBehaviorName" value="PROPAGATION_REQUIRES_NEW"/> </bean> <bean id="PROPAGATION_REQUIRES_NEW-2" class="org.apache.camel.spring.spi.SpringTransactionPolicy"> <property name="transactionManager" ref="transactionManager"/> <property name="propagationBehaviorName" value="PROPAGATION_REQUIRES_NEW"/> </bean>
The preceding two policy
blocks are identical except for their id
attributes. The following code example shows how you could reference the preceding policy
definitions:
<route> <from uri="direct:policies"/> <setHeader headerName="message"> <simple>${body}</simple> </setHeader> <policy ref="PROPAGATION_REQUIRES_NEW"> <to uri="sql:insert into messages (message) values(:#message)"/> <to uri="direct:nestedPolicy"/> <to uri="mock:out1"/> </policy> </route> <route> <from uri="direct:nestedPolicy"/> <policy ref="PROPAGATION_REQUIRES_NEW-2"> <to uri="sql:insert into audit_log (message) values(:#message)"/> <to uri="mock:out2"/> </policy> </route>
3.139.83.62