[jboss-cvs] jboss-seam/doc/reference/en/modules ...
Gavin King
gavin.king at jboss.com
Sun Jan 28 00:49:56 EST 2007
User: gavin
Date: 07/01/28 00:49:56
Modified: doc/reference/en/modules configuration.xml
conversations.xml
Added: doc/reference/en/modules persistence.xml
Log:
split out persistence chapter
updated configuration chapter
Revision Changes Path
1.33 +186 -313 jboss-seam/doc/reference/en/modules/configuration.xml
(In the diff below, changes in quantity of whitespace are not shown.)
Index: configuration.xml
===================================================================
RCS file: /cvsroot/jboss/jboss-seam/doc/reference/en/modules/configuration.xml,v
retrieving revision 1.32
retrieving revision 1.33
diff -u -b -r1.32 -r1.33
--- configuration.xml 5 Dec 2006 20:28:27 -0000 1.32
+++ configuration.xml 28 Jan 2007 05:49:56 -0000 1.33
@@ -49,7 +49,7 @@
<para>
If you are using Sun's JSF 1.2 reference implementation, you should
- add this to <literal>faces-config.xml</literal>:
+ also add this to <literal>faces-config.xml</literal>:
</para>
<programlisting><![CDATA[<application>
@@ -98,10 +98,8 @@
that Seam can use to calculate the JNDI name from the EJB name.
Unfortunately, there is no standard mapping to global JNDI
defined in the EJB3 specification, so this mapping is
- vendor-specific. We must specify a pattern using the configuration
- property named <literal>org.jboss.seam.core.init.jndiPattern</literal>.
- We may specify this using <literal>components.xml</literal>,
- <literal>web.xml</literal> or even <literal>seam.properties</literal>.
+ vendor-specific. We usually specify this option in
+ <literal>components.xml</literal>.
</para>
<para>
@@ -111,36 +109,24 @@
<programlisting><![CDATA[<core:init jndi-name="myEarName/#{ejbName}/local" />]]></programlisting>
<para>
- Or:
- </para>
-
- <programlisting><![CDATA[<context-param>
- <param-name>org.jboss.seam.core.init.jndiPattern</param-name>
- <param-value>myEarName/#{ejbName}/local</param-value>
-</context-param>]]></programlisting>
-
- <para>
Where <literal>myEarName</literal> is the name of the EAR in which
the bean is deployed.
</para>
<para>
- Outside the context of an EAR (when using
- the JBoss Embeddable EJB3 container), the following pattern is
- the one to use:
+ Outside the context of an EAR (when using the JBoss Embeddable EJB3 container),
+ the following pattern is the one to use:
</para>
<programlisting><![CDATA[<core:init jndi-name="#{ejbName}/local" />]]></programlisting>
<para>
- Or:
+ You'll have to experiment to find the right setting for other application
+ servers. Note that some servers (such as GlassFish) require you to specify
+ JNDI names for all EJB components explicitly (and tediously). In this case,
+ you can pick your own pattern ;-)
</para>
- <programlisting><![CDATA[<context-param>
- <param-name>org.jboss.seam.core.init.jndiPattern</param-name>
- <param-value>#{ejbName}/local</param-value>
-</context-param>]]></programlisting>
-
</sect2>
<sect2>
@@ -152,7 +138,7 @@
you need to register a servlet filter:
</para>
-<programlisting><![CDATA[<filter>
+ <programlisting><![CDATA[<filter>
<filter-name>Seam Redirect Filter</filter-name>
<filter-class>org.jboss.seam.servlet.SeamRedirectFilter</filter-class>
</filter>
@@ -192,6 +178,33 @@
</sect2>
+ <sect2>
+ <title>Don't forget!</title>
+
+ <para>
+ There is one final item you need to know about. You must place a <literal>seam.properties</literal>,
+ <literal>META-INF/seam.properties</literal> or <literal>META-INF/components.xml</literal>
+ file in any archive in which your Seam components are deployed (even an empty
+ properties file will do). At startup, Seam will scan any archives with
+ <literal>seam.properties</literal> files for seam components.
+ </para>
+
+ <para>
+ That's why all the Seam examples have an empty <literal>seam.properties</literal>
+ file. You can't just delete this file and expect everything to still work!
+ </para>
+
+ <para>
+ You might think this is silly and what kind of idiot framework designers would
+ make an empty file affect the behavior of their software?? Well, this is a
+ workaround for a limitation of the JVM—if we didn't use this mechanism,
+ our next best option would be to force you to list every component explicitly
+ in <literal>components.xml</literal>, just like some other competing
+ frameworks do! I think you'll like our way better.
+ </para>
+
+ </sect2>
+
</sect1>
<sect1>
@@ -208,14 +221,7 @@
<para>
If you're running in a Java EE 5 environment, this is all the configuration
- required to start using Seam! But there is one final item you need to know about.
- You must place a <literal>seam.properties</literal>,
- <literal>META-INF/seam.properties</literal> or <literal>META-INF/components.xml</literal>
- file in any archive in which your Seam components are deployed (even an empty
- properties file will do). At startup, Seam will scan any archives with
- <literal>seam.properties</literal> files for seam components. If that doesn't
- work for you, you can also add components by installing them explicitly via
- <literal>components.xml</literal>. (We don't recommend this alternative approach.)
+ required to start using Seam!
</para>
<sect2>
@@ -281,7 +287,9 @@
<para>
If you want to use the Seam tag library (most Seam applications do), you must include
<literal>jboss-seam-ui.jar</literal> in the <literal>WEB-INF/lib</literal> directory
- of the WAR.
+ of the WAR. If you want to use the PDF or email tag libraries, you need to put
+ <literal>jboss-seam-pdf.jar</literal> or <literal>jboss-seam-mail.jar</literal>
+ in <literal>WEB-INF/lib</literal>.
</para>
<para>
@@ -307,7 +315,8 @@
</sect1>
<sect1>
- <title>Configuring Seam with the JBoss Embeddable EJB3 container</title>
+ <title>Configuring Seam in Java SE, with the JBoss Embeddable EJB3 container</title>
+
<para>
The JBoss Embeddable EJB3 container lets you run EJB3 components outside the context
of the Java EE 5 application server. This is especially, but not only, useful for testing.
@@ -480,129 +489,11 @@
</sect1>
<sect1>
- <title>Seam managed transactions</title>
- <para>
- EJB session beans feature declarative transaction management. The EJB container is able
- to start a transaction transparently when the bean is invoked, and end it when the
- invocation ends. If we write a session bean method that acts as a JSF action listener,
- we can do all the work associated with that action in one transaction, and be sure that
- it is committed or rolled back when we finish processing the action. This is a great feature,
- and all that is needed by many Seam applications.
- </para>
-
- <para>
- There is just one problem with this approach. ORM solutions like Hibernate and EJB 3.0
- persistence support lazy fetching of entity associations inside a transaction context,
- but throw <literal>LazyInitializationException</literal>s if you try to access an unfetched
- association outside the context of a transaction. This is a problem if your view page
- tries to access data that was not fetched during the transaction. Hibernate users developed
- the <emphasis>open session in view</emphasis> pattern to work around this problem. This
- pattern is usually implemented as a transaction which spans the entire request. There are
- several problems with this idea, the most serious being that we can't be sure that a
- transaction has been successful until we commit it, but by the time we commit the transaction,
- we have already rendered the view. Furthermore, this is at best a partial solution to the
- problem, because we can still meet the dreaded <literal>LazyInitializationException</literal>
- if we try to re-use the entity object in the next request.
- </para>
-
- <para>
- Seam <emphasis>completely solves</emphasis> the problem of unwanted
- <literal>LazyInitializationException</literal>s, while working around the biggest problem
- in the <emphasis>open session in view</emphasis> pattern. The solution comes in two parts:
- </para>
+ <title>Configuring Seam in J2EE</title>
- <itemizedlist>
- <listitem>
- <para>
- use an extended persistence context that is scoped to the conversation,
- instead of to the request
- </para>
- </listitem>
- <listitem>
- <para>
- use two transactions per request; the first spans the beginning of the update model
- values phase until the end of the invoke application phase; the second spans the
- render response phase
- </para>
- </listitem>
- </itemizedlist>
-
- <sect2>
- <title>Enabling Seam-managed transactions</title>
-
- <para>
- To make use of <emphasis>Seam managed transactions</emphasis>, you need to use
- <literal>SeamExtendedManagedPersistencePhaseListener</literal> in place of
- <literal>SeamPhaseListener</literal>.
- </para>
-
- <programlisting><![CDATA[<lifecycle>
- <phase-listener>
- org.jboss.seam.jsf.SeamExtendedManagedPersistencePhaseListener
- </phase-listener>
-</lifecycle>]]></programlisting>
-
- <para>
- It's also a good idea to add a servlet filter to rollback uncommitted transactions
- when uncaught exceptions occur.
- </para>
-
-<programlisting><![CDATA[<filter>
- <filter-name>Seam Exception Filter</filter-name>
- <filter-class>org.jboss.seam.servlet.SeamExceptionFilter</filter-class>
-</filter>
-
-<filter-mapping>
- <filter-name>Seam Exception Filter</filter-name>
- <url-pattern>*.jsf</url-pattern>
-</filter-mapping>]]></programlisting>
-
- </sect2>
-
- <sect2>
- <title>Using a Seam-managed persistence context</title>
-
- <para>
- You'll need to use a <emphasis>managed persistence context</emphasis> (for EJB3) or
- <emphasis>managed session</emphasis> (for Hibernate) in your components. We'll see how to
- use a managed session later. Configuring a managed persistence context is easy. In
- <literal>components.xml</literal>, we can write:
- </para>
-
- <programlisting><![CDATA[<core:managed-persistence-context name="bookingDatabase" auto-create="true"
- persistence-unit-jndi-name="java:/EntityManagerFactories/bookingData"/>]]></programlisting>
-
- <para>
- This configuration creates a conversation-scoped Seam component named
- <literal>bookingDatabase</literal> that manages the lifecycle of <literal>EntityManager</literal>
- instances for the persistence unit (<literal>EntityManagerFactory</literal> instance)
- with JNDI name <literal>java:/EntityManagerFactories/bookingData</literal>.
- </para>
-
- <para>
- Of course, you need to make sure that you have bound the <literal>EntityManagerFactory</literal>
- into JNDI. In JBoss, you can do this by adding the following property setting to
- <literal>persistence.xml</literal>.
- </para>
-
- <programlisting><![CDATA[<property name="jboss.entity.manager.factory.jndi.name"
- value="java:/EntityManagerFactories/bookingData"/>]]></programlisting>
-
- <para>
- Now we can have our <literal>EntityManager</literal> injected using:
- </para>
-
- <programlisting><![CDATA[@In EntityManager bookingDatabase;]]></programlisting>
-
- </sect2>
-
- </sect1>
-
- <sect1>
- <title>Configuring Seam with Hibernate in Java EE</title>
<para>
Seam is useful even if you're not yet ready to take the plunge into EJB 3.0. In this
- case you would use Hibernate3 instead of EJB 3.0 persistence, and plain JavaBeans
+ case you would use Hibernate3 or JPA instead of EJB 3.0 persistence, and plain JavaBeans
instead of session beans. You'll miss out on some of the nice features of session
beans but it will be very easy to migrate to EJB 3.0 when you're ready and, in the
meantime, you'll be able to take advantage of Seam's unique declarative state
@@ -623,14 +514,15 @@
beans do. You <emphasis>could</emphasis> manage your transactions manually using the
JTA <literal>UserTransaction</literal> (you could even implement your own declarative
transaction management in a Seam interceptor). But most applications will use Seam managed
- transactions when using Hibernate with JavaBeans. Follow the instructions above to
- enable <literal>SeamExtendedManagedPersistencePhaseListener</literal>.
+ transactions when using Hibernate with JavaBeans. Follow the instructions in the
+ persistence chapter to install <literal>TransactionalSeamPhaseListener</literal>.
</para>
<para>
The Seam distribution includes a version of the booking example application that
- uses Hibernate and JavaBeans instead of EJB3. This example application is ready to
- deploy into any J2EE application server.
+ uses Hibernate3 and JavaBeans instead of EJB3, and another version that uses
+ JPA and JavaBeans. These example applications are ready to deploy into any J2EE
+ application server.
</para>
<sect2>
@@ -638,57 +530,34 @@
<para>
Seam will bootstrap a Hibernate <literal>SessionFactory</literal> from your
- <literal>hibernate.cfg.xml</literal> file if you install the built-in component
- named <literal>org.jboss.seam.core.hibernate</literal>.
+ <literal>hibernate.cfg.xml</literal> file if you install a built-in component:
</para>
- </sect2>
-
- <sect2>
- <title>Using a Seam-managed Hibernate Session</title>
-
- <para>
- We will also need to configure a <emphasis>managed session</emphasis> if we want a Seam
- managed Hibernate <literal>Session</literal> to be available via injection.
- </para>
+ <programlisting><![CDATA[<core:hibernate-session-factory name="hibernateSessionFactory"/>]]></programlisting>
<para>
- To configure our Seam component, as usual, we use <literal>components.xml</literal>:
+ You will also need to configure a <emphasis>managed session</emphasis> if you want
+ a Seam managed Hibernate <literal>Session</literal> to be available via injection.
</para>
- <programlisting><![CDATA[<core:managed-hibernate-session name="hibernateSessionFactory"/>
+ </sect2>
-<core:managed-hibernate-session name="bookingDatabase" auto-create="true"
- session-factory-jndi-name="java:/bookingSessionFactory"/>]]></programlisting>
+ <sect2>
+ <title>Boostrapping JPA in Seam</title>
<para>
- Where <literal>java:/bookingSessionFactory</literal> is the name of the session factory
- specified in <literal>hibernate.cfg.xml</literal>.
+ Seam will bootstrap a JPA <literal>EntityManagerFactory</literal> from your
+ <literal>persistence.xml</literal> file if you install this built-in component:
</para>
- <programlisting><![CDATA[<session-factory name="java:/bookingSessionFactory">
- <property name="transaction.flush_before_completion">true</property>
- <property name="connection.release_mode">after_statement</property>
- <property name="transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup</property>
- <property name="transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</property>
- <property name="connection.datasource">java:/bookingDatasource</property>
- ...
-</session-factory>]]></programlisting>
-
- <para>
- Note that Seam does not flush the session, so you should always enable
- <literal>hibernate.transaction.flush_before_completion</literal> to
- ensure that the session is automatically flushed before the JTA transaction
- commits.
- </para>
+ <programlisting><![CDATA[<core:entity-manager-factory name="entityManagerFactory"/>]]></programlisting>
<para>
- We can now have a managed Hibernate <literal>Session</literal> injected into our
- JavaBean components using the following code:
+ You will also need to configure a <emphasis>managed persistencece context</emphasis>
+ if you want a Seam managed JPA <literal>EntityManager</literal> to be available via
+ injection.
</para>
- <programlisting><![CDATA[@In Session bookingDatabase;]]></programlisting>
-
</sect2>
<sect2>
@@ -712,6 +581,7 @@
el-ri.jar
jsf-facelets.jar
hibernate3.jar
+ hibernate-annotations.jar
...
my-application.jar/
META-INF/
@@ -739,15 +609,15 @@
</sect1>
<sect1>
- <title>Configuring Seam with Hibernate in Java SE</title>
+ <title>Configuring Seam in Java SE, with the JBoss Microcontainer</title>
<para>
- The Seam support for Hibernate requires JTA and a JCA datasource. If you are running in
- a non-EE environment like Tomcat or TestNG, you can run these services, and Hibernate
- itself, in the JBoss Microcontainer.
+ The Seam support for Hibernate and JPA requires JTA and a JCA datasource. If you are
+ running in a non-EE environment like Tomcat or TestNG you can run these services,
+ and Hibernate itself, in the JBoss Microcontainer.
</para>
<para>
- You can even deploy the Hibernate version of the booking example in Tomcat.
+ You can even deploy the Hibernate and JPA versions of the booking example in Tomcat.
</para>
<mediaobject>
@@ -791,7 +661,7 @@
JTA, your JCA datasource and Hibernate into the microcontainer:
</para>
-<programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+ <programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:jboss:bean-deployer bean-deployer_1_0.xsd"
@@ -823,10 +693,10 @@
</constructor>
</bean>
- <bean name="bookingDatabaseFactory" class="org.jboss.seam.microcontainer.HibernateFactory"/>
- <bean name="bookingDatabase" class="java.lang.Object">
+ <bean name="bookingSessionFactoryFactory" class="org.jboss.seam.microcontainer.HibernateFactory"/>
+ <bean name="bookingSessionFactory" class="java.lang.Object">
<constructor factoryMethod="getSessionFactory">
- <factory bean="bookingDatabaseFactory"/>
+ <factory bean="bookingSessionFactoryFactory"/>
</constructor>
<depends>bookingDatasource</depends>
</bean>
@@ -940,6 +810,7 @@
<sect2>
<title>Packaging</title>
+
<para>
There is not yet any well-defined packaging format for jBPM configuration
and process/pageflow definition files. In the Seam examples we've decided
@@ -1013,10 +884,12 @@
<para>
In addition, you'll need to use a portlet-specific phase listener
instead of <literal>SeamPhaseListener</literal> or
- <literal>SeamExtendedManagedPersistencePhaseListener</literal>.
+ <literal>TransactionalSeamPhaseListener</literal>.
The <literal>SeamPortletPhaseListener</literal> and
- <literal>SeamExtendedManagedPersistencePortletPhaseListener</literal>
- are adapted to the portlet lifecycle.
+ <literal>TransactionalSeamPortletPhaseListener</literal>
+ are adapted to the portlet lifecycle. I would like to offer my
+ sincerest apologies for the name of that last class. I really
+ couldn't think of anything better. Sorry.
</para>
</sect1>
1.23 +0 -67 jboss-seam/doc/reference/en/modules/conversations.xml
(In the diff below, changes in quantity of whitespace are not shown.)
Index: conversations.xml
===================================================================
RCS file: /cvsroot/jboss/jboss-seam/doc/reference/en/modules/conversations.xml,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -b -r1.22 -r1.23
--- conversations.xml 17 Jan 2007 03:14:29 -0000 1.22
+++ conversations.xml 28 Jan 2007 05:49:56 -0000 1.23
@@ -814,73 +814,6 @@
</section>
<section>
- <title>Seam-managed persistence contexts and atomic conversations</title>
- <para>
- Seam provides built-in components for EJB 3.0 and Hibernate persistence context
- management that support the use of persistence contexts scoped to the conversation.
- This useful feature allows you to program optimistic transactions that span multiple
- requests to the server without the need to use the merge() operation or to re-load
- data at the beginning of each request, and without the need to wrestle with the
- dreaded <literal>LazyInitializationException</literal> or
- <literal>NonUniqueObjectException</literal>. Please see the configuration chapter
- for information about configuring Seam-managed persistence contexts and
- Seam-managed transactions.
- </para>
- <para>
- As with any optimistic transaction management, transaction isolation and consistency
- can be achieved via use of optimistic locking. Fortunately, both Hibernate and EJB
- 3.0 make it very easy to use optimistic locking, by providing the
- <literal>@Version</literal> annotation.
- </para>
- <para>
- By default, the persistence context is flushed (synchronized with the database)
- at the end of each transaction. This is sometimes the desired behavior. But very
- often, we would prefer that all changes are held in memory and only written to
- the database when the conversation ends successfully. This allows for truly
- atomic conversations. As the result of a truly stupid and shortsighted decision
- by certain non-JBoss, non-Sun and non-Sybase members of the EJB 3.0 expert group,
- there is currently no simple, usable and portable way to implement atomic
- conversations using EJB 3.0 persistence. However, Hibernate provides this feature
- as a vendor extension to the <literal>FlushModeType</literal>s defined by the
- specification, and it is our expectation that other vendors will soon provide
- a similar extension.
- </para>
- <para>
- Seam lets you specify <literal>FlushModeType.MANUAL</literal> when beginning a
- conversation. Currently, this works only when Hibernate is the underlying
- persistence provider, but we plan to support other equivalent vendor extensions.
- </para>
-
- <programlisting><![CDATA[@In EntityManager em; //a Seam-managed persistence context
-
- at Begin(flushMode=MANUAL)
-public void beginClaimWizard() {
- claim = em.find(Claim.class, claimId);
-}]]></programlisting>
-
- <para>
- Now, the <literal>claim</literal> object remains managed by the persistence context
- for the rest ot the conversation. We can make changes to the claim:
- </para>
-
- <programlisting><![CDATA[public void addPartyToClaim() {
- Party party = ....;
- claim.addParty(party);
-}]]></programlisting>
-
- <para>
- But these changes will not be flushed to the database until we explicitly force
- the flush to occur:
- </para>
-
- <programlisting><![CDATA[@End
-public void commitClaim() {
- em.flush();
-}]]></programlisting>
-
- </section>
-
- <section>
<title>Seam and Servlets</title>
<para>
1.1 date: 2007/01/28 05:49:56; author: gavin; state: Exp;jboss-seam/doc/reference/en/modules/persistence.xml
Index: persistence.xml
===================================================================
<chapter id="persistence">
<title>Seam and Object/Relational Mapping</title>
<para>
Seam provides extensive support for the two most popular persistence
architectures for Java: Hibernate3, and the Java Persistence API
introduced with EJB 3.0. Seam's unique state-management architecture
allows the most sophisticated ORM integration of any web application
framework.
</para>
<section>
<title>Introduction</title>
<para>
Seam grew out of the frustration of the Hibernate team with the
statelessness typical of the previous generation of Java application
architectures. The state management architecture of Seam was originally
designed to solve problems relating to persistence—in particular
problems associated with <emphasis>optimistic transaction processing</emphasis>.
Scalable online applications always use optimistic transactions. An atomic
(database/JTA) level transaction should not span a user interaction unless
the application is designed to support only a very small number of concurrent
clients. But almost all interesting work involves first displaying data
to a user, and then, slightly later, updating the same data. So Hibernate was
designed to support the idea of a persistence context which spanned an
optimistic transaction.
</para>
<para>
Unfortunately, the so-called "stateless" architectures that preceded Seam and
EJB 3.0 had no construct for representing an optimistic transaction. So, instead,
these architectures provided persistence contexts scoped to the atomic
transaction. Of course, this resulted in many problems for users, and is the
cause of the number one user complaint about Hibernate: the dreaded
<literal>LazyInitializationException</literal>. What we need is a construct
for representing an optimistic transaction in the application tier.
</para>
<para>
EJB 3.0 recognizes this problem, and introduces the idea of a stateful
component (a stateful session bean) with an <emphasis>extended persistence
context</emphasis> scoped to the lifetime of the component. This is a
partial solution to the problem (and is a useful construct in and of
itself) however there are two problems:
</para>
<itemizedlist>
<listitem>
<para>
The lifecycle of the stateful session bean must be managed manually
via code in the web tier (it turns out that this is a subtle problem
and much more difficult in practice than it sounds).
</para>
</listitem>
<listitem>
<para>
Propagation of the persistence context between stateful components
in the same optimistic transaction is possible, but tricky.
</para>
</listitem>
</itemizedlist>
<para>
Seam solves the first problem by providing conversations, and stateful
session bean components scoped to the conversation. (Most conversations
actually represent optimistic transactions in the data layer.) This is
sufficient for many simple applications (such as the Seam booking
demo) where persistence context propagation is not needed. For more
complex applications, with many loosly-interacting components in each
conversation, propagation of the persistence context across components
becomes an important issue. So Seam extends the persistence context
management model of EJB 3.0, to provide conversation-scoped extended
persistence contexts.
</para>
</section>
<section>
<title>Seam managed transactions</title>
<para>
EJB session beans feature declarative transaction management. The EJB container is able
to start a transaction transparently when the bean is invoked, and end it when the
invocation ends. If we write a session bean method that acts as a JSF action listener,
we can do all the work associated with that action in one transaction, and be sure that
it is committed or rolled back when we finish processing the action. This is a great feature,
and all that is needed by many Seam applications.
</para>
<para>
There is just one problem with this approach. ORM solutions like Hibernate and EJB 3.0
persistence support lazy fetching of entity associations inside a transaction context,
but throw <literal>LazyInitializationException</literal>s if you try to access an unfetched
association outside the context of a transaction. This is a problem if your view page
tries to access data that was not fetched during the transaction. Hibernate users developed
the <emphasis>open session in view</emphasis> pattern to work around this problem. This
pattern is usually implemented as a transaction which spans the entire request. There are
several problems with this idea, the most serious being that we can't be sure that a
transaction has been successful until we commit it, but by the time we commit the transaction,
we have already rendered the view. Furthermore, this is at best a partial solution to the
problem, because we can still meet the dreaded <literal>LazyInitializationException</literal>
if we try to re-use the entity object in the next request.
</para>
<para>
Seam <emphasis>completely solves</emphasis> the problem of unwanted
<literal>LazyInitializationException</literal>s, while working around the biggest problem
in the <emphasis>open session in view</emphasis> pattern. The solution comes in two parts:
</para>
<itemizedlist>
<listitem>
<para>
use an extended persistence context that is scoped to the conversation,
instead of to the request
</para>
</listitem>
<listitem>
<para>
use two transactions per request; the first spans the beginning of the update model
values phase until the end of the invoke application phase; the second spans the
render response phase
</para>
</listitem>
</itemizedlist>
<section>
<title>Enabling Seam-managed transactions</title>
<para>
To make use of <emphasis>Seam managed transactions</emphasis>, you need to use
<literal>TransactionalSeamPhaseListener</literal> in place of
<literal>SeamPhaseListener</literal>.
</para>
<programlisting><![CDATA[<lifecycle>
<phase-listener>
org.jboss.seam.jsf.TransactionalSeamPhaseListener
</phase-listener>
</lifecycle>]]></programlisting>
<para>
It's also a good idea to add a servlet filter to rollback uncommitted transactions
when uncaught exceptions occur. (According to the Java EE specification, the
web container should do this automatically, but we've found that this behavior
cannot be relied upon in all application servers.)
</para>
<programlisting><![CDATA[<filter>
<filter-name>Seam Exception Filter</filter-name>
<filter-class>org.jboss.seam.servlet.SeamExceptionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Seam Exception Filter</filter-name>
<url-pattern>*.jsf</url-pattern>
</filter-mapping>]]></programlisting>
<para>
Seam transaction management is useful even if you're using EJB 3.0
container-managed persistence contexts. But it is especially useful
if you use Seam outside a Java EE 5 environment, or in any other
case where you would use a Seam-managed persistence context.
</para>
</section>
</section>
<section>
<title>Seam-managed persistence contexts</title>
<para>
If you're using Seam outside of a Java EE 5 environment, you can't rely upon the
container to manage the persistence context lifecycle for you. Even if you are
in an EE 5 environment, you might have a complex application with many loosly
coupled components that collaborate together in the scope of a single conversation,
and in this case you might find that propagation of the persistence context between
component is tricky and error-prone.
</para>
<para>
In either case, you'll need to use a <emphasis>managed persistence context</emphasis>
(for JPA) or a <emphasis>managed session</emphasis> (for Hibernate) in your components.
A Seam-managed persistence context is just a built-in Seam component that manages an
instance of <literal>EntityManager</literal> or <literal>Session</literal> in the
conversation context. You can inject it with <literal>@In</literal>.
</para>
<section>
<title>Using a Seam-managed persistence context with JPA</title>
<para>
Configuring a managed persistence context is easy. In <literal>components.xml</literal>,
we can write:
</para>
<programlisting><![CDATA[<core:managed-persistence-context name="bookingDatabase"
auto-create="true"
persistence-unit-jndi-name="java:/EntityManagerFactories/bookingData"/>]]></programlisting>
<para>
This configuration creates a conversation-scoped Seam component named
<literal>bookingDatabase</literal> that manages the lifecycle of <literal>EntityManager</literal>
instances for the persistence unit (<literal>EntityManagerFactory</literal> instance)
with JNDI name <literal>java:/EntityManagerFactories/bookingData</literal>.
</para>
<para>
Of course, you need to make sure that you have bound the <literal>EntityManagerFactory</literal>
into JNDI. In JBoss, you can do this by adding the following property setting to
<literal>persistence.xml</literal>.
</para>
<programlisting><![CDATA[<property name="jboss.entity.manager.factory.jndi.name"
value="java:/EntityManagerFactories/bookingData"/>]]></programlisting>
<para>
Now we can have our <literal>EntityManager</literal> injected using:
</para>
<programlisting><![CDATA[@In EntityManager bookingDatabase;]]></programlisting>
</section>
<section>
<title>Using a Seam-managed Hibernate session</title>
<para>
Seam-managed Hibernate sessions are similar. In <literal>components.xml</literal>:
</para>
<programlisting><![CDATA[<core:hibernate-session-factory name="hibernateSessionFactory"/>
<core:managed-hibernate-session name="bookingDatabase"
auto-create="true"
session-factory-jndi-name="java:/bookingSessionFactory"/>]]></programlisting>
<para>
Where <literal>java:/bookingSessionFactory</literal> is the name of the session factory
specified in <literal>hibernate.cfg.xml</literal>.
</para>
<programlisting><![CDATA[<session-factory name="java:/bookingSessionFactory">
<property name="transaction.flush_before_completion">true</property>
<property name="connection.release_mode">after_statement</property>
<property name="transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup</property>
<property name="transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</property>
<property name="connection.datasource">java:/bookingDatasource</property>
...
</session-factory>]]></programlisting>
<para>
Note that Seam does not flush the session, so you should always enable
<literal>hibernate.transaction.flush_before_completion</literal> to
ensure that the session is automatically flushed before the JTA transaction
commits.
</para>
<para>
We can now have a managed Hibernate <literal>Session</literal> injected into our
JavaBean components using the following code:
</para>
<programlisting><![CDATA[@In Session bookingDatabase;]]></programlisting>
</section>
<section>
<title>Seam-managed persistence contexts and atomic conversations</title>
<para>
Persistence contexts scoped to the conversation allows you to program optimistic
transactions that span multiple requests to the server without the need to use the
<literal>merge()</literal> operation , without the need to re-load
data at the beginning of each request, and without the need to wrestle with the
<literal>LazyInitializationException</literal> or
<literal>NonUniqueObjectException</literal>.
</para>
<para>
As with any optimistic transaction management, transaction isolation and consistency
can be achieved via use of optimistic locking. Fortunately, both Hibernate and EJB
3.0 make it very easy to use optimistic locking, by providing the
<literal>@Version</literal> annotation.
</para>
<para>
By default, the persistence context is flushed (synchronized with the database)
at the end of each transaction. This is sometimes the desired behavior. But very
often, we would prefer that all changes are held in memory and only written to
the database when the conversation ends successfully. This allows for truly
atomic conversations. As the result of a truly stupid and shortsighted decision
by certain non-JBoss, non-Sun and non-Sybase members of the EJB 3.0 expert group,
there is currently no simple, usable and portable way to implement atomic
conversations using EJB 3.0 persistence. However, Hibernate provides this feature
as a vendor extension to the <literal>FlushModeType</literal>s defined by the
specification, and it is our expectation that other vendors will soon provide
a similar extension.
</para>
<para>
Seam lets you specify <literal>FlushModeType.MANUAL</literal> when beginning a
conversation. Currently, this works only when Hibernate is the underlying
persistence provider, but we plan to support other equivalent vendor extensions.
</para>
<programlisting><![CDATA[@In EntityManager em; //a Seam-managed persistence context
@Begin(flushMode=MANUAL)
public void beginClaimWizard() {
claim = em.find(Claim.class, claimId);
}]]></programlisting>
<para>
Now, the <literal>claim</literal> object remains managed by the persistence context
for the rest ot the conversation. We can make changes to the claim:
</para>
<programlisting><![CDATA[public void addPartyToClaim() {
Party party = ....;
claim.addParty(party);
}]]></programlisting>
<para>
But these changes will not be flushed to the database until we explicitly force
the flush to occur:
</para>
<programlisting><![CDATA[@End
public void commitClaim() {
em.flush();
}]]></programlisting>
</section>
</section>
<section>
<title>Using the JPA "delegate"</title>
<para>
The <literal>EntityManager</literal> interface let's you access a vendor-specific
API via the <literal>getDelegate()</literal> method. Naturally, the most interesting
vendor is Hibernate, and the most powerful delegate interface is
<literal>org.hibernate.Session</literal>. You'd be nuts to use anything else. Trust
me, I'm not biased at all.
</para>
<para>
But regardless of whether you're using Hibernate (genius!) or something else
(masochist, or just not very bright), you'll almost certainly want to use the
delegate in your Seam components from time to time. One approach would be the
following:
</para>
<programlisting><![CDATA[@In EntityManager entityManager;
@Create
public void init() {
( (Session) entityManager.getDelegate() ).enableFilter("currentVersions");
}]]></programlisting>
<para>
But typecasts are unquestionably the ugliest syntax in the Java language, so most
people avoid them whenever possible. Here's a different way to get at the
delegate. First, add the following line to <literal>components.xml</literal>:
</para>
<programlisting><![CDATA[<factory name="session"
scope="STATELESS"
auto-create="true"
value="#{entityManager.delegate}"/>]]></programlisting>
<para>
Now we can inject the session directly:
</para>
<programlisting><![CDATA[@In Session session;
@Create
public void init() {
session.enableFilter("currentVersions");
}]]></programlisting>
</section>
</chapter>
More information about the jboss-cvs-commits
mailing list