Monday, June 6, 2011

Oracle SCA Spring Bean Transactional Behavior

My last blog was about Oracle SCA Spring Beans with own database access instead of using the provided JCA DB Adapter. It’s always a tradeoff to use the Oracle JCA Database Adapter or to implement own database access by the means of the Spring Framework. The DB Adapter in combination with the JDeveloper configuration wizard is easy to use and enable DML SQLs like quick data retrieval. Behind the DB Adapter runs the JPA persistence framework Oracle Toplink and the application could benefit from Toplink (Unit of Work Concept, declarative query result set caching, etc.). On the other side complex SQL commands are difficult if not impossible to build with the DB Adapter. Some SQL commands are even not supported on the DB Adapter (for example set-operators). Therefore it’s very helpful to build the SQL command in a programmatic way without any adapter limitations on the Spring Bean. Additionally if a Spring Bean uses a DB Adapter the performance is suffering from the XML marshaling overhead. A direct database access inside of the Spring Bean is much more efficient.

But in both ways it’s important to understand the transactional behavior inside of the Oracle SCA Container for Spring Beans. Transactional behavior could be influenced by declarative transaction management on the Spring Bean context configuration or in a programmatic way. For Spring Beans there is no property configuration available on the SCDL (like for example the bpel.config.transaction property for BPEL process transaction configuration).

Spring Beans used on Oracle SCA Composites are always taking part in running transactions as default behavior. If there is no caller transaction running, the Spring Bean Component starts a new transaction. This behavior needs no additional configuration and would be the desired behavior in most use cases.

If the Spring Bean should deviate from the default transactional behavior the Spring Beans needs some special configuration on the context configuration file. Therefore a transactional proxy has to be defined on Spring Bean context. The bean definition should state the transaction manager that should be used and the transactional attributes for the service object (referenced by the target property).
The following example is showing a transactional proxy configuration for the Calculator Service Bean. The example assumes existing audit tracking method(s) which should not take part in running transactions (PROPAGATION_REQUIRES_NEW).
clip_image002

The PROPAGATION_REQUIRES_NEW transactional attribute uses a completely independent transaction and will not affect outer transactions. Set the key attribute on “*” in order to set the transaction attribute for all methods.

This is a list of valid transaction attributes supported by the Spring Framework:
  • PROPAGATION_MANDATORY: Supports a current transaction; throw an exception if no current transaction exists.
  • PROPAGATION_NESTED: Executes within a nested transaction if a current transaction exists, behaves like PROPAGATION_REQUIRED else.
  • PROPAGATION_NEVER: Does not support a current transaction; throws an exception if a current transaction exists.
  • PROPAGATION_NOT_SUPPORTED: Does not support a current transaction; rather always executes non-transactionally.
  • PROPAGATION_REQUIRED: Supports a current transaction; creates a new one if none exists.
  • PROPAGATION_REQUIRES_NEW: Creates a new transaction, suspends the current transaction if one exists.
  • PROPAGATION_SUPPORTS: Supports a current transaction; executes non-transactionally if none exists.
This approach could even manipulate the isolation level of transactions. Therefore you have to set on the Spring transaction manager bean the allowCustomIsolationLevels property flag to true (the default value is false). Afterwards you could specify the custom isolation level on the transactional attributes settings. For more details please take a look at the excellent developerWorks article “Specify custom isolation levels with the Spring Framework”.

Another alternative way to the declarative solution is the programmatic approach. A Spring Bean can get a reference to the running transaction, whenever the Spring Bean needs to have programmatic access to the transaction in order to do commit, rollback, suspend or resume a running transaction.

The following listing shows an example on how a Spring Bean could get access of the running transaction. In this example the auditTracking operation is not part of the running global transaction.

Transaction transaction = null;
TransactionManager txMg = null;

try {
ctx = new InitialContext();

   // getting the transaction manager
   txMg = (TransactionManager)

      ctx.lookup("javax.transaction.TransactionManager");

   // suspending the thread's transaction
   transaction = txMg.suspend();


   // call audit tracking method  

   // this operation is not part of the global transaction
   auditTracking(msg);
} catch (NamingException e) {

} catch (Exception e) {

} finally {
try {
   // resuming the transaction
      txMg.resume(transaction);
   } catch (…) {     


}
Listing: Programmatic access to the running transaction

Note that in the example above, the auditTracking operation on the given example will not start an own transaction by default. This is a programmatic approach and the programmer has to take care of all kinds of transaction aspects. In my opinion the declarative approach is much more elegant because it lets the code free of any transactional requirements and centralize the configuration on the Spring Bean context.

In a composite to composite calling scenario take care that Local Optimization happens (take a look on the blog from Ramkumar Menon on how to check if Local Optimization is kicking in the WS Binding) because otherwise the normal composite to composite transaction propagation is not supported. The Oracle documentation mentions that even WS-AT will not help in this scenario (chapter 35.1.1.1.2: “WS-AT transactions are not supported in composite-to-composite calls, even with the oracle.webservices.local.optimization property set to false.”). I’m not sure if this statement is in general true.

Thanks to Clemens Utschig-Utschig who was pointing me on the declarative solution. Also thanks to Silviu Leahu who figured out the programmatic solution.

That’s it. For more details take a look at the Spring JDBC documentation.

No comments: