Seam Managed Persistence Contexts
From Shrubbery
- Enabling the equivalent of 'session-in-view' with Seam
Overview
Seam comes with some built in components that support the 'session-in-view' pattern: Seam Managed Persistence Contexts. Actually it's much more correct than the 'session-in-view' pattern, but that's besides the point. Seam-managed persistence contexts can be used to eliminate lazy initialization exceptions when evaluating JSTL-EL expressions during view rendering as well as to bind all the activity in a conversation together under a single optimistic transaction.
See the docos here: http://docs.jboss.com/seam/latest/reference/en/html/persistence.html
There are a few subtle things that this doesn't explicitly cover:
- You can use the Seam-managed EntityManager in EJBs - This will eliminate some of the need for using merge(obj) because the EJBs can use the conversational EntityManager. However, in cases where you have a transaction-scoped EntityManager you will still need to merge or load objects that came from a different EntityManager.
- The Seam Application Framework classes are designed to use Seam-managed transactions
Steps
- Enable the TransactionalSeamPhaseListener in faces-config.xml
<lifecycle> <!-- We use the extended persistence context because we're going to take advantage of persistence contexts (hibernate sessions) that are conversation scoped (span transactions). This matches what the user may think is a "transaction", but in reality it may be many underlying database transactions. --> <phase-listener>org.jboss.seam.jsf.TransactionalSeamPhaseListener</phase-listener> </lifecycle> - Enable a Seam-managed persistence context in components.xml
<core:transactionListener/> <core:managed-persistence-context name="entityManager" auto-create="true" persistence-unit-jndi-name="java:/EntityManagerFactories/myDb"/>Assuming you have a persistence.xml something like this:
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence"> <persistence-unit name="myDb"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <jta-data-source>java:/MyDataSource</jta-data-source> <properties> <!-- Register the entity manager factory in JNDI so SEAM can use it. --> <property name="jboss.entity.manager.factory.jndi.name" value="java:/EntityManagerFactories/myDb"/> <!-- Comment this out once we switch to true migration --> <!--<property name="hibernate.hbm2ddl.auto" value="create"/>--> <property name="hibernate.show_sql" value="false"/> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLInnoDBDialect"/> <property name="com.intellij.javaee.persistence.datasource" value="Mysql local"/> </properties> </persistence-unit> </persistence> - Inject the Seam-managed EntityManager into your components using @In (instead of the usual @PersistenceContext), for example:
@Stateless public class MyEJBImpl implements MyEJB { ... @In private EntityManager entityManager; ... }
But session-in-view is an anti-pattern, isn't it?
Yes, it is! However what Seam is doing is not really session-in-view. Actually Seam provides two sessions:
- A session that exists during the postback phases of JSF.
- A separate session used for rendering the next view.
This eliminates the problem with the ordinary session-in-view pattern where the rendering part of the web request cycle affects the persistence context / database transaction done in the controller. Incidentally this is why a session-in-view design should never be used in an enterprise-class application (IMO) because presumably the user thinks their data is actually important and would not like an inconsistent database.

