diff --git a/spring-webflow/.classpath b/spring-webflow/.classpath
index e92101ae..d372b1ec 100644
--- a/spring-webflow/.classpath
+++ b/spring-webflow/.classpath
@@ -33,5 +33,7 @@
- * This implementation uses raw Hibernate APIs and binds the current session to
- * the thread-local location identified by Spring's
- * HibernateTransactionManager.
+ * This implementation uses raw Hibernate APIs and binds the current session to the thread-local location identified by
+ * Spring's HibernateTransactionManager.
*
- * This listener assumes that you are accessing Hibernate via Spring support - * such as HibernateTemplate or the LocalSessionFactoryBean. If not, Hibernate - * data access code will not participate in the proper transaction. + * This listener assumes that you are accessing Hibernate via Spring support such as HibernateTemplate or the + * LocalSessionFactoryBean. If not, Hibernate data access code will not participate in the proper transaction. *
- * Note that when accessing service layer methods with Spring managed
- * transactions, those transaction should have {@link Propagation#REQUIRED}
- * semantics. Anything else defeats the purpose of holding the transaction open
- * until the end of the session.
+ * Note that when accessing service layer methods with Spring managed transactions, those transaction should have
+ * {@link Propagation#REQUIRED} semantics. Anything else defeats the purpose of holding the transaction open until the
+ * end of the session.
*
* @author Ben Hale
* @since 1.1
*/
public class HibernateSessionPerConversationListener extends FlowExecutionListenerAdapter {
- private static final String HIBERNATE_SESSION = "hibernate.session";
+ private static final String HIBERNATE_SESSION_ATTRIBUTE = "hibernate.session";
- private SessionFactory sessionFactory;
+ private SessionFactory sessionFactory;
- /**
- * Create a new Session-per-Conversation listener using giving Hibernate session
- * factory.
- * @param sessionFactory the session factory to use
- */
- public HibernateSessionPerConversationListener(SessionFactory sessionFactory) {
- this.sessionFactory = sessionFactory;
- }
+ /**
+ * Create a new Session-per-Conversation listener using giving Hibernate session factory.
+ * @param sessionFactory the session factory to use
+ */
+ public HibernateSessionPerConversationListener(SessionFactory sessionFactory) {
+ this.sessionFactory = sessionFactory;
+ }
- /**
- * When a {@link FlowSession} is created a Hibernate Session
- * is opened and put into MANUAL flush mode. From there it is bound to the
- * conversation scope of this {@link FlowSession}. Since
- * {@link #resumed(RequestContext)} will not be called on start of the
- * session, this method also binds the session to the thread-local location.
- * This behavior will only be exhibited on the root flow in a {@link FlowExecution}.
- */
- public void sessionCreated(RequestContext context, FlowSession session) {
- if (session.isRoot()) {
- Session hibSession = createSession(context);
- bindSession(hibSession);
- }
+ public void sessionCreated(RequestContext context, FlowSession session) {
+ if (session.isRoot()) {
+ Session hibernateSession = createSession(context);
+ context.getConversationScope().put(HIBERNATE_SESSION_ATTRIBUTE, hibernateSession);
+ bind(hibernateSession, context);
}
+ }
- /**
- * When a {@link FlowExecution} is resumed, the conversationally scoped
- * Session is bound to the thread-local location and a
- * transaction is opened.
- */
- public void resumed(RequestContext context) {
- Session hibSession = getHibernateSession(context);
- bindSession(hibSession);
- }
+ public void resumed(RequestContext context) {
+ Session hibSession = getHibernateSession(context);
+ bind(hibSession, context);
+ }
- /**
- * When a {@link FlowExecution} is paused the conversationally scoped
- * Session is unbound from the thread-local location and the
- * transaction is committed. The transaction that is closed is a Hibernate
- * transaction and with a FlushMode of MANUAL will not flush anything to the
- * database.
- */
- public void paused(RequestContext context, ViewSelection selectedView) {
- Session hibSession = getHibernateSession(context);
- unBindSession(hibSession);
- }
+ public void paused(RequestContext context, ViewSelection selectedView) {
+ Session session = getHibernateSession(context);
+ unbind(session, context);
+ }
- /**
- * When a {@link FlowSession} is destroyed all changes are flushed to the
- * database, the current transaction committed, and the Hibernate
- * Session is closed. Since
- * {@link #paused(RequestContext, ViewSelection)} will not be called on the
- * ending of this session, this method also unbinds the session from the
- * thread-local location. This behavior will only be exhibited on the root
- * flow in a {@link FlowExecution}.
- */
- public void sessionEnded(RequestContext context, FlowSession session, AttributeMap output) {
- if (session.isRoot()) {
- Session hibSession = (Session) context.getConversationScope().remove(HIBERNATE_SESSION);
- hibSession.flush();
- unBindSession(hibSession);
- destroySession(hibSession);
- }
+ public void sessionEnded(RequestContext context, FlowSession session, AttributeMap output) {
+ if (session.isRoot()) {
+ Session hibernateSession = (Session) context.getConversationScope().remove(HIBERNATE_SESSION_ATTRIBUTE);
+ hibernateSession.flush();
+ unbind(hibernateSession, context);
+ hibernateSession.close();
}
-
- /**
- * When an exception is thrown from a {@link FlowExecution}, the
- * conversationally scoped Session is unbound from the
- * thread-local location and the transaction is committed. The
- * transaction that is closed is a Hibernate transaction and with a
- * FlushMode of MANUAL will not flush anything to the database.
- */
- public void exceptionThrown(RequestContext context, FlowExecutionException exception) {
- Session hibSession = getHibernateSession(context);
- unBindSession(hibSession);
- }
-
- // internal helpers
+ }
- private Session createSession(RequestContext context) {
- Session hibSession = sessionFactory.openSession();
- hibSession.setFlushMode(FlushMode.MANUAL);
- context.getConversationScope().put(HIBERNATE_SESSION, hibSession);
- return hibSession;
- }
+ public void exceptionThrown(RequestContext context, FlowExecutionException exception) {
+ Session session = getHibernateSession(context);
+ unbind(session, context);
+ }
- private void destroySession(Session hibSession) {
- hibSession.close();
- }
+ // internal helpers
- private Session getHibernateSession(RequestContext context) {
- return (Session) context.getConversationScope().get(HIBERNATE_SESSION);
- }
+ private Session createSession(RequestContext context) {
+ Session session = sessionFactory.openSession();
+ session.setFlushMode(FlushMode.MANUAL);
+ return session;
+ }
- private void bindSession(Session hibSession) {
- Transaction hibTx = hibSession.beginTransaction();
- SessionHolder sessionHolder = new SessionHolder(hibSession);
- sessionHolder.setTransaction(hibTx);
- TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
- }
+ private Session getHibernateSession(RequestContext context) {
+ return (Session) context.getConversationScope().get(HIBERNATE_SESSION_ATTRIBUTE);
+ }
- private void unBindSession(Session hibSession) {
- hibSession.getTransaction().commit();
- TransactionSynchronizationManager.unbindResource(sessionFactory);
+ private void bind(Session hibSession, RequestContext context) {
+ SessionHolder sessionHolder = new SessionHolder(hibSession);
+ if (context.getActiveFlow().getAttributes().getBoolean("transactional").booleanValue() == true) {
+ Transaction tx = hibSession.beginTransaction();
+ sessionHolder.setTransaction(tx);
}
-}
+ TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
+ }
+
+ private void unbind(Session hibSession, RequestContext context) {
+ if (context.getActiveFlow().getAttributes().getBoolean("transactional").booleanValue() == true) {
+ hibSession.getTransaction().commit();
+ }
+ TransactionSynchronizationManager.unbindResource(sessionFactory);
+ }
+}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/support/persistence/JpaSessionPerConversationListener.java b/spring-webflow/src/main/java/org/springframework/webflow/support/persistence/JpaSessionPerConversationListener.java
new file mode 100644
index 00000000..99f6fd45
--- /dev/null
+++ b/spring-webflow/src/main/java/org/springframework/webflow/support/persistence/JpaSessionPerConversationListener.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2004-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.webflow.support.persistence;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+
+import org.springframework.orm.jpa.EntityManagerHolder;
+import org.springframework.transaction.support.TransactionSynchronizationManager;
+import org.springframework.webflow.core.collection.AttributeMap;
+import org.springframework.webflow.execution.FlowExecutionException;
+import org.springframework.webflow.execution.FlowExecutionListener;
+import org.springframework.webflow.execution.FlowExecutionListenerAdapter;
+import org.springframework.webflow.execution.FlowSession;
+import org.springframework.webflow.execution.RequestContext;
+import org.springframework.webflow.execution.ViewSelection;
+
+/**
+ * A {@link FlowExecutionListener} that implements the Session-per-Conversation using the standard Java Persistence API
+ * (JPA).
+ *
+ * This implementation uses standard JPA APIs. The general pattern is as follows: + *