Skip to content

Instantly share code, notes, and snippets.

@seykron
Created February 12, 2013 15:36
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save seykron/4770724 to your computer and use it in GitHub Desktop.
Save seykron/4770724 to your computer and use it in GitHub Desktop.
import java.util.Arrays;
import java.util.List;
import javax.transaction.Synchronization;
import org.apache.commons.lang.Validate;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.context.internal.ManagedSessionContext;
import org.hibernate.context.spi.CurrentSessionContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.service.jta.platform.spi.JtaPlatform;
import org.springframework.orm.hibernate4.SpringSessionContext;
import org.springframework.transaction.jta.JtaAfterCompletionSynchronization;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support
.TransactionSynchronizationAdapter;
import org.springframework.transaction.support
.TransactionSynchronizationManager;
/** Session context that determines whether there exist a transaction in
* progress in the current thread, and if not, it opens the {@link Session}.
* <p>
* It delegates to {@link SpringSessionContext} to check whether there exist
* a Session or not. If it doesn't, the Session is created bound to the current
* thread via {@link ManagedSessionContext}.
* </p>
* <p>
* Sessions created by this context are registered in the available transaction
* synchronization strategy in order to cleanup and properly close Sessions
* when transactions finish. If there's no synchronization strategy available,
* the session will never be closed.
* </p>
* @author Matias Mirabelli
*/
public class TransactionAwareSessionContext implements CurrentSessionContext {
/** ID for serialization.
*/
private static final long serialVersionUID = -4213662197614198364L;
/** Hibernate session factory; it's never null. */
private final SessionFactoryImplementor sessionFactory;
/** Default session context to use before creating a new session;
* it's never null. */
private final SpringSessionContext defaultSessionContext;
/** Context to store configured sessions; it's never null. */
private final ManagedSessionContext localSessionContext;
/** Creates a new session context and sets the related session factory.
*
* @param theSessionFactory Context session factory. Cannot be null.
*/
public TransactionAwareSessionContext(
final SessionFactoryImplementor theSessionFactory) {
Validate.notNull(theSessionFactory, "The session factory cannot be null.");
defaultSessionContext = new SpringSessionContext(theSessionFactory);
localSessionContext = new ManagedSessionContext(theSessionFactory);
sessionFactory = theSessionFactory;
}
/** Binds the configured session to Spring's transaction manager strategy
* if there's no session.
*
* @return Returns the configured session, or the one managed by Spring.
* Never returns null.
*/
public Session currentSession() {
try {
return defaultSessionContext.currentSession();
} catch (HibernateException cause) {
// There's no session bound to the current thread. Let's open one if
// needed.
if (ManagedSessionContext.hasBind(sessionFactory)) {
return localSessionContext.currentSession();
}
Session session;
session = sessionFactory.openSession();
if (registerSynchronization(session)) {
// Normalizes Session flush mode, defaulting it to AUTO. Required for
// synchronization.
FlushMode flushMode = session.getFlushMode();
if (FlushMode.isManualFlushMode(flushMode)
&& !TransactionSynchronizationManager
.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.AUTO);
}
}
ManagedSessionContext.bind(session);
return session;
}
}
/** Registers transaction synchronization with session in order to clean
* up and close the session when transaction finishes.
*
* @param session Session to register into transaction synchronization.
* Cannot be null.
* @return Returns <code>true</code> if the session was register into any
* available synchronization strategy, <code>false</code> otherwise.
*/
private boolean registerSynchronization(final Session session) {
// Tries Spring's transaction manager synchronization.
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// If it's allowed, registers synchronization to cleanup session.
TransactionSynchronizationManager.registerSynchronization(
createTransactionSynchronization(session));
return true;
} else {
// Tries JTA transaction manager synchronization.
JtaPlatform jtaPlatform = sessionFactory.getServiceRegistry()
.getService(JtaPlatform.class);
// If it's allowed, registers synchronization to cleanup session.
if (jtaPlatform.canRegisterSynchronization()) {
List<TransactionSynchronization> synchronizations;
synchronizations = Arrays.asList(
createTransactionSynchronization(session));
Synchronization jtaSync;
jtaSync = new JtaAfterCompletionSynchronization(synchronizations);
jtaPlatform.registerSynchronization(jtaSync);
return true;
}
}
return false;
}
/** Creates a transaction synchronization object for the specified session.
* @param session Session to synchronize using the created object. Cannot be
* null.
* @return A valid transaction synchronization. Never returns null.
*/
private TransactionSynchronization createTransactionSynchronization(
final Session session) {
return new TransactionSynchronizationAdapter() {
@Override
public void afterCompletion(final int status) {
session.close();
ManagedSessionContext.unbind(sessionFactory);
}
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment