SeamFramework.orgCommunity Documentation
Seam fornisce un supporto esteso alle due maggiori e più popolari architetture per la persistenza in Java: Hibernate3 e Java Persistence API introdotta con EJB 3.0. L'architettura unica di Seam per la gestione dello stato consente l'integrazione dei più sofisticati ORM di ogni framework per applicazioni web.
Seam è nato dalla frustrazione del team di Hibernate per l'assenza di contesto stateless tipica delle precedenti generazioni di architetture nelle applicazioni Java. L'architettura della gestione dello stato di Seam è stata originariamente progettata per risolvere problemi relativi alla persistenza — in particolare i problemi associati all'elaborazione ottimistica delle transazioni. Le applicazioni online scalabili usano sempre transazioni ottimistiche. Una transazione atomica di livello (database/JTA) non dovrebbe propagare l'interazione dell'utente amenoché l'applicazione sia progettata per supportare solo un piccolo numero di client concorrenti. Ma quasi tutto il lavoro interessante coinvolge in primo luogo la visualizzazione dei dati all'utente, e poi, immediatamente dopo, l'aggiornamento dei dati stessi. Quindi Hibernate è stato progettato per supportare l'idea del contesto di persistenza che propaga una transazione ottimistica.
Sfortunatamente le cosiddette architetture "stateless" che precedettero Seam e EJB 3.0 non avevano alcun costrutto per rappresentare una transazione ottimistica. Quindi, invece, queste architetture fornivano contesti di persistenza con scope a livello di transazione atomica. Sicuramente questo portava diversi problemi agli utenti ed è la causa numero uno per le lamentele riguardanti Hibernate: la temuta LazyInitializationException
. Ciò di cui si ha bisogno è un costrutto per rappresentare una transazione ottimistica a livello applicazione.
EJB 3.0 riconosce il problema e introduce l'idea di componente stateful (un bean di sessione stateful) con un contesto di persistenza esteso con scope legato al ciclo di vita del componente. Questa è una soluzione parziale al problema (ed è un utile costrutto), comunque ci sono due problemi:
Il ciclo di vita del bean di sessione stateful deve essere gestito manualmente via codice a livello web (risulta che questo è un problema sottile e molto più difficile in pratica di quanto sembri).
La propagazione del contesto di persistenza tra componenti stateful nella stessa transazione ottimistica è possibile, ma pericolosa.
Seam risolve il primo problema fornendo conversazioni, componenti bean di sessione stateful con scope di conversazione. (La maggior parte delle conversazioni in verità rappresentano transazioni ottimistiche a livello dei dati). Questo è sufficiente per molte semplici applicazioni (quali la demo prenotazione di Seam) dove non serve la propagazione del contesto di persistenza. Per applicazioni più complesse, con molti componenti interagenti in modo stretto in ciascuna conversazione, la propagazione del contesto di persistenza tra componenti diventa un problema importante. Quindi Seam estende il modello di gestione del contesto di persistenza di EJB 3.0 per fornire contesti di persistenza estesi e con scope di conversazione.
I bean di sessione EJB includono la gestione dichiarativa delle transazioni. Il container EJB è capace di avviare una transazione in modo trasparente quando viene invocato il bean, e terminarla quando termina l'invocazione. Se si scrive un metodo di un bean di sessione che agisce come action listener JSF, si può fare tutto il lavoro associato all'azione in una transazione, ed essere sicuri che venga eseguito il commit od il rollback quando l'azione viene terminata. Questa è grande funzionalità ed è tutto ciò che serve ad alcune applicazioni Seam.
Comunque c'è un problema con tale approccio. Un'applicazione Seam potrebbe non eseguire l'accesso a tutti i dati per una richiesta da una chiamata di un singolo metodo a un bean di sessione.
La richiesta può comportare di essere processata da diversi componenti poco accoppiati, ciascuno dei quali viene chiamato indipendentemente dal layer web. E' comune vedere parecchie chiamate per richiesta dal layer web ai componenti EJB in Seam.
La generazione della vista può richiedere il lazy fetching delle associazioni.
Più transazioni per richiesta ci sono, più è probabile che si incontrino problemi di atomicità e isolamento quando l'applicazione processa molte richieste concorrenti. Certamente tutte le operazioni di scrittura devono avvenire nella stessa transazione!
Hibernate users developed the "open session in view" pattern to work around this problem. In the Hibernate community, "open session in view" was historically even more important because frameworks like Spring use transaction-scoped persistence contexts. So rendering the view would cause LazyInitializationException
s when unfetched associations were accessed.
This pattern is usually implemented as a single transaction which spans the entire request. There are several problems with this implementation, the most serious being that we can never be sure that a transaction is successful until we commit it — but by the time the "open session in view" transaction is committed, the view is fully rendered, and the rendered response may already have been flushed to the client. How can we notify the user that their transaction was unsuccessful?
Seam solves both the transaction isolation problem and the association fetching problem, while working around the problems with "open session in view". The solution comes in two parts:
use an extended persistence context that is scoped to the conversation, instead of to the transaction
use two transactions per request; the first spans the beginning of the restore view phase (some transaction managers begin the transaction later at the beginning of the apply request vaues phase) until the end of the invoke application phase; the second spans the render response phase
In the next section, we'll tell you how to set up a conversation-scope persistence context. But first we need to tell you how to enable Seam transaction management. Note that you can use conversation-scoped persistence contexts without Seam transaction management, and there are good reasons to use Seam transaction management even when you're not using Seam-managed persistence contexts. However, the two facilities were designed to work together, and work best when used together.
La gestione delle transazioni di Seam è utile anche se vengono usati contesti di persistenza gestiti da un container EJB 3.0. Ma in particolare essa è utile quando Seam è usato fuori dall'ambiente Java EE 5, o in ogni altro caso dove si usi un contesto di persistenza gestito da Seam.
Seam transaction management is enabled by default for all JSF requests. If you want to disable this feature, you can do it in components.xml
:
<core:init transaction-management-enabled="false"/>
<transaction:no-transaction />
Seam provides a transaction management abstraction for beginning, committing, rolling back, and synchronizing with a transaction. By default Seam uses a JTA transaction component that integrates with Container Managed and programmatic EJB transactions. If you are working in a Java EE 5 environment, you should install the EJB synchronization component in components.xml
:
<transaction:ejb-transaction />
However, if you are working in a non EE 5 container, Seam will try auto detect the transaction synchronization mechanism to use. However, if Seam is unable to detect the correct transaction synchronization to use, you may find you need configure one of the following:
JPA RESOURCE_LOCAL transactions with the javax.persistence.EntityTransaction
interface. EntityTransaction
begins the transaction at the beginning of the apply request values phase.
Hibernate managed transactions with the org.hibernate.Transaction
interface. HibernateTransaction
begins the transaction at the beginning of the apply request values phase.
Spring managed transactions with the org.springframework.transaction.PlatformTransactionManager
interface. The Spring PlatformTransactionManagement
manager may begin the transaction at the beginning of the apply request values phase if the userConversationContext
attribute is set.
Explicitly disable Seam managed transactions
Configure JPA RESOURCE_LOCAL transaction management by adding the following to your components.xml where #{em}
is the name of the persistence:managed-persistence-context
component. If your managed persistence context is named entityManager
, you can opt to leave out the entity-manager
attribute. (see Seam-managed persistence contexts )
<transaction:entity-transaction entity-manager="#{em}"/>
To configure Hibernate managed transactions declare the following in your components.xml where #{hibernateSession}
is the name of the project's persistence:managed-hibernate-session
component. If your managed hibernate session is named session
, you can opt to leave out the session
attribute. (see Seam-managed persistence contexts )
<transaction:hibernate-transaction session="#{hibernateSession}"/>
To explicitly disable Seam managed transactions declare the following in your components.xml:
<transaction:no-transaction />
For configuring Spring managed transactions see using Spring PlatformTransactionManagement .
Transaction synchronization provides callbacks for transaction related events such as beforeCompletion()
and afterCompletion()
. By default, Seam uses it's own transaction synchronization component which requires explicit use of the Seam transaction component when committing a transaction to ensure synchronization callbacks are correctly executed. If in a Java EE 5 environment the <transaction:ejb-transaction/>
component should be be declared in components.xml
to ensure that Seam synchronization callbacks are correctly called if the container commits a transaction outside of Seam's knowledge.
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.
In either case, you'll need to use a managed persistence context (for JPA) or a managed session (for Hibernate) in your components. A Seam-managed persistence context is just a built-in Seam component that manages an instance of EntityManager
or Session
in the conversation context. You can inject it with @In
.
Seam-managed persistence contexts are extremely efficient in a clustered environment. Seam is able to perform an optimization that EJB 3.0 specification does not allow containers to use for container-managed extended persistence contexts. Seam supports transparent failover of extended persisence contexts, without the need to replicate any persistence context state between nodes. (We hope to fix this oversight in the next revision of the EJB spec.)
Configuring a managed persistence context is easy. In components.xml
, we can write:
<persistence:managed-persistence-context name="bookingDatabase"
auto-create="true"
persistence-unit-jndi-name="java:/EntityManagerFactories/bookingData"/>
This configuration creates a conversation-scoped Seam component named bookingDatabase
that manages the lifecycle of EntityManager
instances for the persistence unit (EntityManagerFactory
instance) with JNDI name java:/EntityManagerFactories/bookingData
.
Of course, you need to make sure that you have bound the EntityManagerFactory
into JNDI. In JBoss, you can do this by adding the following property setting to persistence.xml
.
<property name="jboss.entity.manager.factory.jndi.name"
value="java:/EntityManagerFactories/bookingData"/>
Now we can have our EntityManager
injected using:
@In EntityManager bookingDatabase;
If you are using EJB3 and mark your class or method @TransactionAttribute(REQUIRES_NEW)
then the transaction and persistence context shouldn't be propagated to method calls on this object. However as the Seam-managed persistence context is propagated to any component within the conversation, it will be propagated to methods marked REQUIRES_NEW
. Therefore, if you mark a method REQUIRES_NEW
then you should access the entity manager using @PersistenceContext.
Le sessioni Hibernate gestite da Seam sono simili. In components.xml
:
<persistence:hibernate-session-factory name="hibernateSessionFactory"/>
<persistence:managed-hibernate-session name="bookingDatabase"
auto-create="true"
session-factory-jndi-name="java:/bookingSessionFactory"/>
Dove java:/bookingSessionFactory
è il nome della session factory specificata in hibernate.cfg.xml
.
<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
>
Note that Seam does not flush the session, so you should always enable hibernate.transaction.flush_before_completion
to ensure that the session is automatically flushed before the JTA transaction commits.
We can now have a managed Hibernate Session
injected into our JavaBean components using the following code:
@In Session bookingDatabase;
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 merge()
operation , without the need to re-load data at the beginning of each request, and without the need to wrestle with the LazyInitializationException
or NonUniqueObjectException
.
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 @Version
annotation.
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 FlushModeType
s defined by the specification, and it is our expectation that other vendors will soon provide a similar extension.
Seam lets you specify FlushModeType.MANUAL
when beginning a conversation. Currently, this works only when Hibernate is the underlying persistence provider, but we plan to support other equivalent vendor extensions.
@In EntityManager em; //a Seam-managed persistence context
@Begin(flushMode=MANUAL)
public void beginClaimWizard() {
claim = em.find(Claim.class, claimId);
}
Now, the claim
object remains managed by the persistence context for the rest ot the conversation. We can make changes to the claim:
public void addPartyToClaim() {
Party party = ....;
claim.addParty(party);
}
But these changes will not be flushed to the database until we explicitly force the flush to occur:
@End
public void commitClaim() {
em.flush();
}
Of course, you could set the flushMode
to MANUAL
from pages.xml, for example in a navigation rule:
<begin-conversation flush-mode="MANUAL" />
Si può impostare qualsiasi Contesto di Persistenza Gestito da Seam alla modalità flush manuale:
<components xmlns="http://jboss.com/products/seam/components" xmlns:core="http://jboss.com/products/seam/core"> <core:manager conversation-timeout="120000" default-flush-mode="manual" /> </components >
The EntityManager
interface lets you access a vendor-specific API via the getDelegate()
method. Naturally, the most interesting vendor is Hibernate, and the most powerful delegate interface is org.hibernate.Session
. You'd be nuts to use anything else. Trust me, I'm not biased at all. If you must use a different JPA provider see Using Alternate JPA Providers.
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:
@In EntityManager entityManager;
@Create
public void init() {
( (Session) entityManager.getDelegate() ).enableFilter("currentVersions");
}
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 components.xml
:
<factory name="session"
scope="STATELESS"
auto-create="true"
value="#{entityManager.delegate}"/>
Ora si può iniettare la sessione direttamente:
@In Session session;
@Create
public void init() {
session.enableFilter("currentVersions");
}
Seam proxies the EntityManager
or Session
object whenever you use a Seam-managed persistence context or inject a container managed persistence context using @PersistenceContext
. This lets you use EL expressions in your query strings, safely and efficiently. For example, this:
User user = em.createQuery("from User where username=#{user.username}")
.getSingleResult();
è equivalente a:
User user = em.createQuery("from User where username=:username")
.setParameter("username", user.getUsername())
.getSingleResult();
Certamente non si dovrà mai e poi mai scrivere qualcosa del tipo:
User user = em.createQuery("from User where username=" + user.getUsername()) //BAD!
.getSingleResult();
(è inefficiente e vulnerabile ad attacchi di SQL injection.)
The coolest, and most unique, feature of Hibernate is filters. Filters let you provide a restricted view of the data in the database. You can find out more about filters in the Hibernate documentation. But we thought we'd mention an easy way to incorporate filters into a Seam application, one that works especially well with the Seam Application Framework.
I contesti di persistenza gestiti da Seam possono avere una lista di filtri definiti, che verrà abilitata quando viene creato un EntityManager
od una Session
di Hibernate. (Certamente possono essere utilizzati solo quando Hibernare è il provider di persistenza sottostante.)
<persistence:filter name="regionFilter">
<persistence:name
>region</persistence:name>
<persistence:parameters>
<key
>regionCode</key>
<value
>#{region.code}</value>
</persistence:parameters>
</persistence:filter>
<persistence:filter name="currentFilter">
<persistence:name
>current</persistence:name>
<persistence:parameters>
<key
>date</key>
<value
>#{currentDate}</value>
</persistence:parameters>
</persistence:filter>
<persistence:managed-persistence-context name="personDatabase"
persistence-unit-jndi-name="java:/EntityManagerFactories/personDatabase">
<persistence:filters>
<value
>#{regionFilter}</value>
<value
>#{currentFilter}</value>
</persistence:filters>
</persistence:managed-persistence-context
>