diff --git a/spring-webflow/src/main/java/org/springframework/webflow/support/persistence/HibernateSessionPerConversationListener.java b/spring-webflow/src/main/java/org/springframework/webflow/support/persistence/HibernateSessionPerConversationListener.java
index 4e5b8243..c0633b0c 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/support/persistence/HibernateSessionPerConversationListener.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/support/persistence/HibernateSessionPerConversationListener.java
@@ -18,10 +18,13 @@ package org.springframework.webflow.support.persistence;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
import org.springframework.orm.hibernate3.SessionHolder;
+import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.execution.FlowExecution;
+import org.springframework.webflow.execution.FlowExecutionException;
import org.springframework.webflow.execution.FlowExecutionListener;
import org.springframework.webflow.execution.FlowExecutionListenerAdapter;
import org.springframework.webflow.execution.FlowSession;
@@ -41,6 +44,11 @@ import org.springframework.webflow.execution.ViewSelection;
* 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 manged
+ * 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
*/
@@ -60,10 +68,13 @@ public class HibernateSessionPerConversationListener extends FlowExecutionListen
* 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) {
- Session hibSession = createSession(context);
- bindSession(hibSession);
+ if (session.isRoot()) {
+ Session hibSession = createSession(context);
+ bindSession(hibSession);
+ }
}
/**
@@ -94,13 +105,28 @@ public class HibernateSessionPerConversationListener extends FlowExecutionListen
* 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.
+ * 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) {
- Session hibSession = (Session) context.getConversationScope().remove(HIBERNATE_SESSION);
- hibSession.flush();
+ if (session.isRoot()) {
+ Session hibSession = (Session) context.getConversationScope().remove(HIBERNATE_SESSION);
+ hibSession.flush();
+ unBindSession(hibSession);
+ destroySession(hibSession);
+ }
+ }
+
+ /**
+ * 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);
- destroySession(hibSession);
}
private Session createSession(RequestContext context) {
@@ -119,8 +145,9 @@ public class HibernateSessionPerConversationListener extends FlowExecutionListen
}
private void bindSession(Session hibSession) {
- hibSession.beginTransaction();
+ Transaction hibTx = hibSession.beginTransaction();
SessionHolder sessionHolder = new SessionHolder(hibSession);
+ sessionHolder.setTransaction(hibTx);
TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
}