Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » engine » transaction » [javadoc | source]
    1   /*
    2    * Hibernate, Relational Persistence for Idiomatic Java
    3    *
    4    * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
    5    * indicated by the @author tags or express copyright attribution
    6    * statements applied by the authors.  All third-party contributions are
    7    * distributed under license by Red Hat Middleware LLC.
    8    *
    9    * This copyrighted material is made available to anyone wishing to use, modify,
   10    * copy, or redistribute it subject to the terms and conditions of the GNU
   11    * Lesser General Public License, as published by the Free Software Foundation.
   12    *
   13    * This program is distributed in the hope that it will be useful,
   14    * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   15    * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
   16    * for more details.
   17    *
   18    * You should have received a copy of the GNU Lesser General Public License
   19    * along with this distribution; if not, write to:
   20    * Free Software Foundation, Inc.
   21    * 51 Franklin Street, Fifth Floor
   22    * Boston, MA  02110-1301  USA
   23    *
   24    */
   25   package org.hibernate.engine.transaction;
   26   
   27   import java.sql.Connection;
   28   import java.sql.SQLException;
   29   import javax.transaction.Transaction;
   30   import javax.transaction.TransactionManager;
   31   
   32   import org.slf4j.Logger;
   33   import org.slf4j.LoggerFactory;
   34   import org.hibernate.HibernateException;
   35   import org.hibernate.engine.SessionImplementor;
   36   import org.hibernate.exception.JDBCExceptionHelper;
   37   
   38   /**
   39    * Class which provides the isolation semantics required by
   40    * an {@link IsolatedWork}.  Processing comes in two flavors:<ul>
   41    * <li>{@link #doIsolatedWork} : makes sure the work to be done is
   42    * performed in a seperate, distinct transaction</li>
   43    * <li>{@link #doNonTransactedWork} : makes sure the work to be
   44    * done is performed outside the scope of any transaction</li>
   45    * </ul>
   46    *
   47    * @author Steve Ebersole
   48    */
   49   public class Isolater {
   50   
   51   	private static final Logger log = LoggerFactory.getLogger( Isolater.class );
   52   
   53   	/**
   54   	 * Ensures that all processing actually performed by the given work will
   55   	 * occur on a seperate transaction.
   56   	 *
   57   	 * @param work The work to be performed.
   58   	 * @param session The session from which this request is originating.
   59   	 * @throws HibernateException
   60   	 */
   61   	public static void doIsolatedWork(IsolatedWork work, SessionImplementor session) throws HibernateException {
   62   		boolean isJta = session.getFactory().getTransactionManager() != null;
   63   		if ( isJta ) {
   64   			new JtaDelegate( session ).delegateWork( work, true );
   65   		}
   66   		else {
   67   			new JdbcDelegate( session ).delegateWork( work, true );
   68   		}
   69   	}
   70   
   71   	/**
   72   	 * Ensures that all processing actually performed by the given work will
   73   	 * occur outside of a transaction.
   74   	 *
   75   	 * @param work The work to be performed.
   76   	 * @param session The session from which this request is originating.
   77   	 * @throws HibernateException
   78   	 */
   79   	public static void doNonTransactedWork(IsolatedWork work, SessionImplementor session) throws HibernateException {
   80   		boolean isJta = session.getFactory().getTransactionManager() != null;
   81   		if ( isJta ) {
   82   			new JtaDelegate( session ).delegateWork( work, false );
   83   		}
   84   		else {
   85   			new JdbcDelegate( session ).delegateWork( work, false );
   86   		}
   87   	}
   88   
   89   	// should be ok performance-wise to generate new delegate instances for each
   90   	// request since these are locally stack-scoped.  Besides, it makes the code
   91   	// much easier to read than the old TransactionHelper stuff...
   92   
   93   	private static interface Delegate {
   94   		public void delegateWork(IsolatedWork work, boolean transacted) throws HibernateException;
   95   	}
   96   
   97   	/**
   98   	 * An isolation delegate for JTA-based transactions.  Essentially susepnds
   99   	 * any current transaction, does the work in a new transaction, and then
  100   	 * resumes the initial transaction (if there was one).
  101   	 */
  102   	public static class JtaDelegate implements Delegate {
  103   		private final SessionImplementor session;
  104   
  105   		public JtaDelegate(SessionImplementor session) {
  106   			this.session = session;
  107   		}
  108   
  109   		public void delegateWork(IsolatedWork work, boolean transacted) throws HibernateException {
  110   			TransactionManager transactionManager = session.getFactory().getTransactionManager();
  111   			Transaction surroundingTransaction = null;
  112   			Connection connection = null;
  113   			boolean caughtException = false;
  114   
  115   			try {
  116   				// First we need to suspend any current JTA transaction and obtain
  117   				// a JDBC connection
  118   				surroundingTransaction = transactionManager.suspend();
  119   				if ( log.isDebugEnabled() ) {
  120   					log.debug( "surrounding JTA transaction suspended [" + surroundingTransaction + "]" );
  121   				}
  122   
  123   				if ( transacted ) {
  124   					transactionManager.begin();
  125   				}
  126   
  127   				connection = session.getBatcher().openConnection();
  128   
  129   				// perform the actual work
  130   				work.doWork( connection );
  131   
  132   				// if everything went ok, commit the transaction and close the obtained
  133   				// connection handle...
  134   				session.getBatcher().closeConnection( connection );
  135   
  136   				if ( transacted ) {
  137   					transactionManager.commit();
  138   				}
  139   			}
  140   			catch( Throwable t ) {
  141   				// at some point the processing went bad, so we need to:
  142   				//      1) make sure the connection handle gets released
  143   				//      2) try to cleanup the JTA context as much as possible
  144   				caughtException = true;
  145   				try {
  146   					if ( connection != null && !connection.isClosed() ) {
  147   						session.getBatcher().closeConnection( connection );
  148   					}
  149   				}
  150   				catch( Throwable ignore ) {
  151   					log.trace( "unable to release connection on exception [" + ignore + "]" );
  152   				}
  153   				if ( transacted ) {
  154   					try {
  155   						transactionManager.rollback();
  156   					}
  157   					catch( Throwable ignore ) {
  158   						log.trace( "unable to rollback new transaction on exception [" + ignore + "]" );
  159   					}
  160   				}
  161   				// finally handle the exception
  162   				if ( t instanceof HibernateException ) {
  163   					throw ( HibernateException ) t;
  164   				}
  165   				else {
  166   					throw new HibernateException( "error performing isolated work", t );
  167   				}
  168   			}
  169   			finally {
  170   				if ( surroundingTransaction != null ) {
  171   					try {
  172   						transactionManager.resume( surroundingTransaction );
  173   						if ( log.isDebugEnabled() ) {
  174   							log.debug( "surrounding JTA transaction resumed [" + surroundingTransaction + "]" );
  175   						}
  176   					}
  177   					catch( Throwable t ) {
  178   						if ( !caughtException ) {
  179   							throw new HibernateException( "unable to resume previously suspended transaction", t );
  180   						}
  181   					}
  182   				}
  183   			}
  184   		}
  185   	}
  186   
  187   	/**
  188   	 * An isolation delegate for JDBC-based transactions.  Basically just
  189   	 * grabs a new connection and does the work on that.
  190   	 */
  191   	public static class JdbcDelegate implements Delegate {
  192   		private final SessionImplementor session;
  193   
  194   		public JdbcDelegate(SessionImplementor session) {
  195   			this.session = session;
  196   		}
  197   
  198   		public void delegateWork(IsolatedWork work, boolean transacted) throws HibernateException {
  199   			Connection connection = null;
  200   			boolean wasAutoCommit = false;
  201   			try {
  202   				connection = session.getBatcher().openConnection();
  203   
  204   				if ( transacted ) {
  205   					if ( connection.getAutoCommit() ) {
  206   						wasAutoCommit = true;
  207   						connection.setAutoCommit( false );
  208   					}
  209   				}
  210   
  211   				work.doWork( connection );
  212   
  213   				if ( transacted ) {
  214   					connection.commit();
  215   				}
  216   			}
  217   			catch( Throwable t ) {
  218   				try {
  219   					if ( transacted && connection != null && !connection.isClosed() ) {
  220   						connection.rollback();
  221   					}
  222   				}
  223   				catch( Throwable ignore ) {
  224   					log.trace( "unable to release connection on exception [" + ignore + "]" );
  225   				}
  226   
  227   				if ( t instanceof HibernateException ) {
  228   					throw ( HibernateException ) t;
  229   				}
  230   				else if ( t instanceof SQLException ) {
  231   					throw JDBCExceptionHelper.convert(
  232   							session.getFactory().getSQLExceptionConverter(),
  233   					        ( SQLException ) t,
  234   					        "error performing isolated work"
  235   					);
  236   				}
  237   				else {
  238   					throw new HibernateException( "error performing isolated work", t );
  239   				}
  240   			}
  241   			finally {
  242   				if ( connection != null ) {
  243   					if ( transacted && wasAutoCommit ) {
  244   						try {
  245   							connection.setAutoCommit( true );
  246   						}
  247   						catch( Throwable ignore ) {
  248   							log.trace( "was unable to reset connection back to auto-commit" );
  249   						}
  250   					}
  251   					session.getBatcher().closeConnection( connection );
  252   				}
  253   			}
  254   		}
  255   	}
  256   }

Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » engine » transaction » [javadoc | source]