Modularizing the Hibernate SessionFactory with the Spring framework

In this article, we describe our modularization and transaction management approach with Spring
and Hibernate. Similar to the OSGi JPA Service Specification, it allows each persistent module of
an enterprise application to have its own individually configured, module-specific SessionFactory.

Consequently, both entity classes and the associated
SessionFactory configuration of a module are isolated from other modules. On one hand,
modularization enforces isolation between modules, but on the other, it increases the need for
coordination between them from within the scope of a transaction.

To avoid the cost and complexity of distributed transaction
processing with an XA protocol, we extended Spring’s transaction management with two concepts:
SessionFactory swapping and transaction synchronization. A single, non-JTA transaction manager
traverses the session factories that are involved in a logical transaction which crosses module
boundaries. With the concept of transaction synchronization, we
implemented the “Shared Transaction Resource” pattern to guarantee the atomicity property of a
logical transaction by using a shared database connection.

After modularizing the global SessionFactory, we measured a higher throughput of transactions
(3-5%) despite the overhead caused by SessionFactory swapping and transaction synchronization.
Furthermore, the module internal session factories can now be configured individually and optimized
for module-specific needs, which was not feasible in the past. We also briefly explain the modularization
approach proposed by OSGi JPA Service Specification and OSGi JTA Transaction Service
Specification and point out what it has in common with our approach.

The intent of this article is to provide insights into the approach we have chosen in order to
receive feedback and comments. We believe in the strength of the approach with regards to
domain-driven concepts and would like to initiate a discussion on this topic.

Modularization and Transaction Management

Modularization is a widely adopted concept for building large-scale applications. Typically,
such an application is decomposed into a number of modules or bundles (the term module and bundle
are used interchangeably here), with each of these modules being responsible for a certain
domain-specific functionality. This type of modularization is also referred to as vertical
modularization, in contrast to (rather coarse-grained) horizontal modularization, where the
application is split by its technical layers (e.g. the persistence layer).

Transactions are a key
component of enterprise applications. When multiple modules are involved in a transaction, the
outcome in each module must be coordinated such that the combined outcome fulfills the atomicity
property of a transaction. A transaction either succeeds when actions in all involved modules
succeed, or rolls back when
one of the modules fail. We define a cross-module transaction as a logical
transaction which spans multiple persistent modules.

Modularization and transaction management are tightly related. On one hand, modularization
enforces isolation between modules, but on the other hand, it increases the cost and complexity of
coordination between them in the scope of a transaction. In this article, we describe our
modularization and transaction management approach with Spring and Hibernate. We briefly describe
the approach proposed by the OSGi
Service Platform Enterprise Specification[3] and point out where the
two approaches are similar.

Modularization with Spring and Hibernate

Despite the increasing support for enterprise applications, migrating
an existing enterprise application to Spring DM or OSGi can still be a very challenging task
due to numerous, unforeseeable issues troubleshooting class loading problems such as
ClassNotFoundExceptions or LinkageErrors [4]. Hence, we decided to stick
with plain Spring and Hibernate and enable a long-term migration path to an OSGi-based
deployment.

To enforce isolation among modules, both the entity classes and the associated persistence
configuration of each module stay inside its module boundary. This is also in line with domain-driven design
concepts [5]. Since session factories serve as persistence configurations
in Hibernate, we remove the need for a shared, global SessionFactory by allowing each persistent
module to have its own individually configured, module-specific SessionFactory (see Figure 1,
SFA stands for the SessionFactory of Module A).

Using multiple session factories in a logical transaction imposes challenging issues concerning
transaction management. In Spring, a transaction manager is configured with exactly one
SessionFactory or one data source when the application starts up. Normally, this binding is static,
and does not change afterwards. We had the following alternatives to handle a cross-module
transaction:

Use multiple transaction managers, and bind each of them to exactly one SessionFactory.

Use a JTA transaction manager which supports distributed transactions with an XA protocol.

Lose the static binding to one SessionFactory, and allow the transaction manager to bind to
different session factories in the scope of a logical transaction.

We quickly abandoned the first two alternatives due to the potential cost and complexity of an
XA
protocol with two-phase commit. Since most of the modules of an application share the same data
source, the imposed cost and complexity are definitively unnecessary.

Let’s look at the third option with a single, non-JTA transaction manager. Without being
statically bound to a single SessionFactory from the application startup time, the transaction
manager traverses the session factories involved in a cross-module transaction. However, by
assuming that there is only one SessionFactory shared by all modules of an application, Spring
creates a new Spring transactionfor each SessionFactory involved in a cross-module
transaction.

Hence, a logical cross-module transaction is mapped to multiple Spring transactions. The
coordination among these Spring transactions remains with this option. There exist different
patterns to realize distributed transactions with and without XA [2]. For
simplicity and higher throughput we chose the “Shared Transaction Resource” pattern which removes
the need for XA by ensuring that all the resources are synchronized or backed by the same resource,
which is in our case here the connection rather than the Hibernate session.

We extended Spring’s transaction management with two main concepts: SessionFactory swapping and
transaction synchronization on connection-level. The concept of SessionFactory swapping allows a
transaction manager to traverse the session factories involved in a cross-module transaction. The
concept of transaction synchronization implements the Shared Transaction Resource pattern.

SessionFactory Swapping

The SessionFactory is "swapped" at module boundaries. Figure 2 illustrates the concept of
SessionFactory swapping with a transaction involving Module A, B and C. Module A starts a
transaction and invokes a service provided by Module B. Module B subsequently invokes a service
provided by Module C. Each module has a module-specific SessionFactory, e.g., the SessionFactory
SFA for Module A, and SFB for Module B respectively.

Upon entering a module, the SessionFactory which is currently bound to the transaction manager
is set aside, and the SessionFactory of the module to be entered gets bound to the transaction
manager (see red arrows on their way entering into modules in Figure 2). When leaving the module,
the transaction manager is restored to the previous SessionFactory (see green arrows on their way
back leaving the modules).

This raises the question of how to know when a module boundary is crossed. Following the
service-orientation in OSGi and Spring DM, each module declares a set of services (i.e. Spring
beans) that it exports and imports. Invocations on an exported service are intercepted by a proxy
object. Thus, the proxy is aware of the boundary crossing, and implements the code for
SessionFactory swapping both before and after invoking the actual service-providing bean.

Transaction Synchronization

Since a logical cross-module transaction is mapped to multiple Spring transactions, we have to
keep track of Spring transactions which belong to a single logical transaction in order to
coordinate among them. As shown in Figure 3, we use a thread-bound propagation stack to track
ongoing Spring transactions, and their parent-child relationship according to their transaction
propagation behavior. In Figure 3, the brackets represent transaction boundaries. txA
stands for transaction A, and is associated with SessionFactory SFA.

Figure 3 A propagation stack for keeping track of Spring transactions belonging
to a logical transaction

By keeping track of transactions which belong to the same logical transaction, we are able to
recognize transactional resources involved in a logical transaction in order to synchronize them.
If the transactional resources share the same data source, they are synchronized by the same database
connection (see Figure 1).

A note on logical transactions

It is worth mentioning that a transaction with a requires-new propagation behavior which is
invoked by an existing transaction is not part of the parent logical transaction. The requires-new
transaction opens its own, new transactional resource (i.e. a database connection) which is not
synchronized with the transactional resources of the outer logical transaction.

More concretely, all child transactions (so from Figure 2, transaction txB and
txC, which are associated with SessionFactory SFB and SFC
respectively) reuse the database connection of its parent transaction (in this case transaction
txA). Technically, Spring opens a new session for transaction txB.
Our extension re-connects this new session to the database connection opened by the parent
transaction txA. Apart from using the same database connection, the completion of all
child transactions is delayed until the completion of the parent transaction. As part of a commit
operation, a flush operation must be performed for all involved sessions.

By default, we assume that modules of an application share the same data source. There are valid
domain-specific scenarios which require a module to use a separate, dedicated data source. For
instance, a module for encryption or security auditing may need to store sensitive encryption
parameters or audit data in a separate database. We have implemented our Spring extensions in a
proprietary framework, and introduced a customized transactional namespace to keep the complexity
out of the application code. Using this customized transactional namespace, a module explicitly
specifies that it does not use the shared database of the application.

Advantages of Modularized Session Factories

Extending Spring’s transaction management with SessionFactory swapping and the Shared
Transaction Resource pattern was a very challenging task. Our extension consists of support for
both declarative and programmatic transaction management, and for transactional tests. After
modularizing the global SessionFactory, we measured a higher throughput of transactions (3-5%) in
spite of the overhead caused by SessionFactory swapping and synchronization of transactions.

With module-specific session factories, the module boundaries become much more explicit.
Listeners and interceptors can be registered to the SessionFactory of a particular module of
interest. They receive far less invocations from the module-specific SessionFactory than from the
global SessionFactory. This variance explains the increased throughput of transactions after
introducing module-specific session factories.

Furthermore, the module internal session factories can now be configured individually to meet
the needs of their module. This for example allows for the use of a dedicated cache, or specific
replication configuration along with other options, all of which was not feasible in the past.

Modularization with OSGi

Since Peter Kriens’s blog [1] about class loading and global
SessionFactory configuration issues when using Hibernate in an OSGi Service Platform, various
solutions have been proposed. Most of them rely on Require-Bundle, Dynamic-Import or Eclipse-Buddy
policy, but do not solve the problem from the ground up. The recent OSGi JPA Service Specification
[3] addresses the issues with a new approach.

According to the specification, a persistence bundle contains the entity classes and
accompanying persistence descriptions (which are comparable to Hibernate SessionFactory
configurations). For each persistence unit in a persistence descriptor, the JPA provider registers
an Entity Manager Factory (Builder) service, which can be retrieved by a client bundle to
manipulate the entity classes. The OSGi JPA approach is comparable to our approach with Spring and
Hibernate.

Once multiple entity managers from different bundles are involved, multiple database sessions or
connections will be opened for a cross-bundle transaction. For the atomicity property of a
transaction, it is of paramount importance to know which transactional resources belong to a
logical transaction. The OSGi JTA Transaction Service Specification [3]
introduces the enlistment strategy. A transactional resource enlists/associates itself with an
ongoing transaction to participate in the resource commit coordination.

Our approach utilizes information such as transaction propagation behavior and underlying data
source to keep track of transactional resources which belong to a logical transaction. The OSGi
specification does not go into details about how XA resources are coordinated, or how the
coordination can be optimized if a shared data source is used. They remain the responsibility of a
concrete JTA provider or the underlying resource manager (e.g. the database). A more in-depth
comparison between our approach and the OSGi strategy is not possible in the scope of this
paper.

We would like to thank Rostislav Georgiev and Thomas Bastian for their insightful thoughts and
intense discussions on this topic. Their in-depth knowledge about Spring and transaction management
gave us a leap forward regarding both the conceptual work and implementation.

About the authors

Dr. Yun Ding and Karsten Klein are Software Architects at InterComponentWare (ICW) AG, an
international company specializing in technology solutions for the healthcare market based in
Walldorf, Germany. All ICW solutions are built on the ICW eHealth Platform, a suite of powerful
technology components that support the quick assembly of eHealth solutions and the flexible
implementation of integration scenarios. Both authors regularly contribute to conferences on
enterprise application and to industry as well as research publications.

TechTarget provides technology professionals with the information they need to perform their jobs - from developing strategy, to making cost-effective purchase decisions and managing their organizations technology projects - with its network of technology-specific websites, events and online magazines.