So far in the Spring into Seam series, you've learned how to build Seam-Spring hybrid components and how to add stateful behavior to Spring beans with help from JBoss Seam. In this final article, you'll learn how to coax Seam and Spring into sharing a single persistence context -- the pinnacle feat of Spring-Seam integration.
Spring and JBoss Seam are both powerful and popular frameworks for Java development -- and they also complement one another. In previous installments in this series (excerpted from Seam in Action) you've seen how to build hybrid components that can participate in both frameworks and learned how Seam can enhance Spring beans by making them stateful. In this third and final article in the series, you'll see that one of Seam's best features -- its conversation-scoped persistence context -- can also solve one of Spring's greatest weaknesses. This feature is by far the most compelling reason for Spring developers to incorporate Seam into their development toolbox. As is customary, I have saved the best for last.
For everything except extending the persistence context, the template classes in Spring's object-relational mapping (ORM) module offer a convenient way to manage the Hibernate
Session or Java Persistence API (JPA)
EntityManager. But where Spring's ORM support breaks down is in its mishandling of the persistence context. Spring can scope the persistence context anywhere from a single operation (method call) to an entire request. But out of the box, it cannot go any further. Spring Web Flow, an extension module for Spring, does offer a solution to manage an extended persistence context over the course of a flow, but Seam's persistence context management is arguably more versatile overall.
Let's look more closely at how Spring shanks the persistence context before diving into the configuration necessary to make the exchange of the Seam-managed persistence context possible. The way the persistence manager (the generic term for a Hibernate
Session or JPA
EntityManager) is handled turns out to be the biggest challenge in the effort to integrate Spring and Seam. It's akin to a custody battle over a child in a divorce. Only in this case, learning how to share the child (the persistence manager) is what ends up saving the marriage.
The problem with Spring's thread-local approach
Spring's ORM support for JPA and Hibernate improved dramatically in Spring 2.0. The JPA support framework can now assume responsibility for injecting an
EntityManager instance into a
@PersistenceContext field on a bean in a non-Java EE environment. (The annotation would typically be handled by the Java EE container.) The Hibernate package now leverages the thread-local Hibernate
Session as returned by the
getCurrentSession() method on the
SessionFactory, by implementing the
CurrentSessionContext contract rather than by introducing yet another proxy layer. However, Spring still imposes one key limitation as custodian of the persistence managers, which interferes with their function in stateful applications: Spring scopes the persistence context to the atomic transaction rather than to the business use case (e.g., the conversation).
Request-scoped persistence contexts
Let's start from the viewpoint of the
OpenEntityManagerInViewFilter. Why is this filter needed in a Spring application? While persistence unit runtimes (e.g.,
EntityManagerFactory) are thread-safe, persistence managers (e.g.,
EntityManager) are not. The former can be stored in the application scope as a singleton. The later must be reserved for a given use case, which has traditionally been a single request. This filter binds the persistence manager to the thread that is processing the request so that the persistence context remains available for all persistence operations in that request. Without this filter, the best Spring can do is ensure that the persistence manager remains available for the lifetime of the transaction. The worst approach you can take with Spring is to allow the persistence manager to run free (not even binding it to a transaction), in which case a new instance is created for each persistence operation!
Since the persistence context is one the most compelling features of ORM, the
OpenEntityManagerInViewFilter is at least a step in the right direction towards maximizing the value you are getting from the ORM. With the filter in place, Spring scopes the persistence manager to the thread using the
bindResource() method of the
TransactionSynchronizationManager, a page from Hibernate's playbook. (Hibernate can perform this work on its own, although the
OpenSessionInViewFilter is still available as an alternative, serving the same role as its JPA counterpart.)
This thread-bound approach gives you the flexibility to lazy-load associations in the view layer from the same persistence manager that retrieved the primary entity instance (meaning that the instances do not become detached at any point during the request). However, the benefit dies with the thread (and, in turn, the request).