Java tutorial
/* * Hibernate, Relational Persistence for Idiomatic Java * * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.context.internal; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.transaction.Synchronization; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import org.hibernate.ConnectionReleaseMode; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.context.spi.AbstractCurrentSessionContext; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper; import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform; import org.hibernate.internal.CoreMessageLogger; import org.jboss.logging.Logger; /** * An implementation of {@link org.hibernate.context.spi.CurrentSessionContext} which scopes the notion * of a current session to a JTA transaction. Because JTA gives us a nice tie-in to clean up after * ourselves, this implementation will generate Sessions as needed provided a JTA transaction is in * effect. If a session is not already associated with the current JTA transaction at the time * {@link #currentSession()} is called, a new session will be opened and it will be associated with that * JTA transaction. * * Note that the sessions returned from this method are automatically configured with both the * {@link org.hibernate.cfg.Environment#FLUSH_BEFORE_COMPLETION auto-flush} and * {@link org.hibernate.cfg.Environment#AUTO_CLOSE_SESSION auto-close} attributes set to true, meaning * that the Session will be automatically flushed and closed as part of the lifecycle for the JTA * transaction to which it is associated. Additionally, it will also be configured to aggressively * release JDBC connections after each statement is executed. These settings are governed by the * {@link #isAutoFlushEnabled()}, {@link #isAutoCloseEnabled()}, and {@link #getConnectionReleaseMode()} * methods; these are provided (along with the {@link #buildOrObtainSession()} method) for easier * subclassing for custom JTA-based session tracking logic (like maybe long-session semantics). * * @author Steve Ebersole */ public class JTASessionContext extends AbstractCurrentSessionContext { private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, JTASessionContext.class.getName()); private transient Map<Object, Session> currentSessionMap = new ConcurrentHashMap<Object, Session>(); /** * Constructs a JTASessionContext * * @param factory The factory this context will service */ public JTASessionContext(SessionFactoryImplementor factory) { super(factory); } @Override public Session currentSession() throws HibernateException { final JtaPlatform jtaPlatform = factory().getServiceRegistry().getService(JtaPlatform.class); final TransactionManager transactionManager = jtaPlatform.retrieveTransactionManager(); if (transactionManager == null) { throw new HibernateException("No TransactionManagerLookup specified"); } Transaction txn; try { txn = transactionManager.getTransaction(); if (txn == null) { throw new HibernateException("Unable to locate current JTA transaction"); } if (!JtaStatusHelper.isActive(txn.getStatus())) { // We could register the session against the transaction even though it is // not started, but we'd have no guarantee of ever getting the map // entries cleaned up (aside from spawning threads). throw new HibernateException("Current transaction is not in progress"); } } catch (HibernateException e) { throw e; } catch (Throwable t) { throw new HibernateException("Problem locating/validating JTA transaction", t); } final Object txnIdentifier = jtaPlatform.getTransactionIdentifier(txn); Session currentSession = currentSessionMap.get(txnIdentifier); if (currentSession == null) { currentSession = buildOrObtainSession(); try { txn.registerSynchronization(buildCleanupSynch(txnIdentifier)); } catch (Throwable t) { try { currentSession.close(); } catch (Throwable ignore) { LOG.debug("Unable to release generated current-session on failed synch registration", ignore); } throw new HibernateException("Unable to register cleanup Synchronization with TransactionManager"); } currentSessionMap.put(txnIdentifier, currentSession); } else { validateExistingSession(currentSession); } return currentSession; } /** * Builds a {@link org.hibernate.context.internal.JTASessionContext.CleanupSync} capable of cleaning up the the current session map as an after transaction * callback. * * @param transactionIdentifier The transaction identifier under which the current session is registered. * @return The cleanup synch. */ private CleanupSync buildCleanupSynch(Object transactionIdentifier) { return new CleanupSync(transactionIdentifier, this); } /** * Strictly provided for subclassing purposes; specifically to allow long-session * support. This implementation always just opens a new session. * * @return the built or (re)obtained session. */ @SuppressWarnings("deprecation") protected Session buildOrObtainSession() { return baseSessionBuilder().autoClose(isAutoCloseEnabled()) .connectionReleaseMode(getConnectionReleaseMode()).flushBeforeCompletion(isAutoFlushEnabled()) .openSession(); } /** * Mainly for subclass usage. This impl always returns true. * * @return Whether or not the the session should be closed by transaction completion. */ protected boolean isAutoCloseEnabled() { return true; } /** * Mainly for subclass usage. This impl always returns true. * * @return Whether or not the the session should be flushed prior transaction completion. */ protected boolean isAutoFlushEnabled() { return true; } /** * Mainly for subclass usage. This impl always returns after_statement. * * @return The connection release mode for any built sessions. */ protected ConnectionReleaseMode getConnectionReleaseMode() { return ConnectionReleaseMode.AFTER_STATEMENT; } /** * JTA transaction sync used for cleanup of the internal session map. */ protected static class CleanupSync implements Synchronization { private Object transactionIdentifier; private JTASessionContext context; public CleanupSync(Object transactionIdentifier, JTASessionContext context) { this.transactionIdentifier = transactionIdentifier; this.context = context; } @Override public void beforeCompletion() { } @Override public void afterCompletion(int i) { context.currentSessionMap.remove(transactionIdentifier); } } }