Sunday, March 14, 2010

Global transactions in Seam

Transactions grew to be a necessity in most today's applications, and managing them grew to be a demanding task. If the application is complex enough it can evolve into a nightmare in its own right. Thus, offloading this task to a framework whenever possible is a very desirable approach.
Being a powerful feature-packed framework that it is, Seam offers exceptional support for transactional behavior that in many instances surpasses all others. One feature not seen in other frameworks are global transactions.

The confusion

This topic is regarded as somewhat arcane, despite being essentially simple. The reason for this is twofold. Firstly, the documentation regarding global transactions is relatively poor. Secondly, and more importantly, the exiting documentation does not call this feature global transactions, and this is the term used most widely over the internet (it was originally coined by Dan Allen is his book Seam in Action, often regarded as the "default" book for Seam).

Clearing it up

In essence, global transactions refer to the two or three transactions that wrap each request in Seam. The first one wraps JSF phases beginning with Restore View (for JTA transactions) or Invoke Application (for resource-local transactions), and ending just before Render Response. The second wraps all page actions (if they are used), and the third wraps the Render Response phase. During this final transaction, context flushing is disabled, effectively rendering the whole transaction read-only.
In this fashion, Seam accomplishes a couple of things:
  • an error in page preparation or rendering will never role back a successfully executed business logic,

  • view rendering is executed atomically, which not only avoids opening and closing a transaction on every lazy initialization, but also guarantees isolation and guards against accidental changes of the state during this phase,

  • page actions are executed atomically (with benefits similar to those mentioned above sans disabled context flushing).

This way, Seam effectively does away with a good portion of the most common problems of transaction management.
To effectively use global transactions, one should keep in mind that Seam turns them on by default. To turn them off, you'll need to disable Seam's transaction management all together. This is done by adding
<core:init transaction-management-enabled="false"/>

to the component descriptor file.


It should also be noted that, despite what can be read in some sources, @Transactional annotation is not unusable in conjunction with global transactions. The point here is that since there is an overarching transaction, no new transactions will be spawned to encompass annotated business methods, effectively ignoring @Transactional. Of course, an overarching transaction will only be present during a JSF postback. On non-JSF calls, @Transactional will still do it's job normally. This misunderstanding was caused by a somewhat ambiguous statement in Seam in Action.
To recap, global translations is just another name for (as Dan Allen would put it) "automatic transaction wrapped around the request". A useful feature which one should always keep in mind when developing applications in Seam.