Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » jdbc » [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.jdbc;
   26   
   27   import java.io.Serializable;
   28   import java.io.ObjectOutputStream;
   29   import java.io.IOException;
   30   import java.io.ObjectInputStream;
   31   import java.sql.Connection;
   32   import java.sql.SQLException;
   33   
   34   import javax.transaction.TransactionManager;
   35   
   36   import org.slf4j.Logger;
   37   import org.slf4j.LoggerFactory;
   38   import org.hibernate.ConnectionReleaseMode;
   39   import org.hibernate.HibernateException;
   40   import org.hibernate.Interceptor;
   41   import org.hibernate.SessionException;
   42   import org.hibernate.Transaction;
   43   import org.hibernate.TransactionException;
   44   import org.hibernate.util.JTAHelper;
   45   import org.hibernate.engine.SessionFactoryImplementor;
   46   import org.hibernate.exception.JDBCExceptionHelper;
   47   import org.hibernate.transaction.CacheSynchronization;
   48   import org.hibernate.transaction.TransactionFactory;
   49   
   50   /**
   51    * Acts as the mediary between "entity-mode related" sessions in terms of
   52    * their interaction with the JDBC data store.
   53    *
   54    * @author Steve Ebersole
   55    */
   56   public class JDBCContext implements Serializable, ConnectionManager.Callback {
   57   
   58   	// TODO : make this the factory for "entity mode related" sessions;
   59   	// also means making this the target of transaction-synch and the
   60   	// thing that knows how to cascade things between related sessions
   61   	//
   62   	// At that point, perhaps this thing is a "SessionContext", and
   63   	// ConnectionManager is a "JDBCContext"?  A "SessionContext" should
   64   	// live in the impl package...
   65   
   66   	private static final Logger log = LoggerFactory.getLogger( JDBCContext.class );
   67   
   68   	public static interface Context extends TransactionFactory.Context {
   69   		/**
   70   		 * We cannot rely upon this method being called! It is only
   71   		 * called if we are using Hibernate Transaction API.
   72   		 */
   73   		public void afterTransactionBegin(Transaction tx);
   74   		public void beforeTransactionCompletion(Transaction tx);
   75   		public void afterTransactionCompletion(boolean success, Transaction tx);
   76   		public ConnectionReleaseMode getConnectionReleaseMode();
   77   		public boolean isAutoCloseSessionEnabled();
   78   	}
   79   
   80   	private Context owner;
   81   	private ConnectionManager connectionManager;
   82   	private transient boolean isTransactionCallbackRegistered;
   83   	private transient Transaction hibernateTransaction;
   84   
   85   	public JDBCContext(Context owner, Connection connection, Interceptor interceptor) {
   86   		this.owner = owner;
   87   		this.connectionManager = new ConnectionManager(
   88   		        owner.getFactory(),
   89   		        this,
   90   		        owner.getConnectionReleaseMode(),
   91   		        connection,
   92   		        interceptor
   93   			);
   94   
   95   		final boolean registerSynchronization = owner.isAutoCloseSessionEnabled()
   96   		        || owner.isFlushBeforeCompletionEnabled()
   97   		        || owner.getConnectionReleaseMode() == ConnectionReleaseMode.AFTER_TRANSACTION;
   98   		if ( registerSynchronization ) {
   99   			registerSynchronizationIfPossible();
  100   		}
  101   	}
  102   
  103   	/**
  104   	 * Private constructor used exclusively for custom serialization...
  105   	 *
  106   	 */
  107   	private JDBCContext() {
  108   	}
  109   
  110   	// ConnectionManager.Callback implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  111   
  112   	public void connectionOpened() {
  113   		if ( owner.getFactory().getStatistics().isStatisticsEnabled() ) {
  114   			owner.getFactory().getStatisticsImplementor().connect();
  115   		}
  116   	}
  117   
  118   	public void connectionCleanedUp() {
  119   		if ( !isTransactionCallbackRegistered ) {
  120   			afterTransactionCompletion( false, null );
  121   			// Note : success = false, because we don't know the outcome of the transaction
  122   		}
  123   	}
  124   
  125   	public SessionFactoryImplementor getFactory() {
  126   		return owner.getFactory();
  127   	}
  128   
  129   	public ConnectionManager getConnectionManager() {
  130   		return connectionManager;
  131   	}
  132   
  133   	public Connection borrowConnection() {
  134   		return connectionManager.borrowConnection();
  135   	}
  136   	
  137   	public Connection connection() throws HibernateException {
  138   		if ( owner.isClosed() ) {
  139   			throw new SessionException( "Session is closed" );
  140   		}
  141   
  142   		return connectionManager.getConnection();
  143   	}
  144   
  145   	public boolean registerCallbackIfNecessary() {
  146   		if ( isTransactionCallbackRegistered ) {
  147   			return false;
  148   		}
  149   		else {
  150   			isTransactionCallbackRegistered = true;
  151   			return true;
  152   		}
  153   
  154   	}
  155   
  156   	public boolean registerSynchronizationIfPossible() {
  157   		if ( isTransactionCallbackRegistered ) {
  158   			// we already have a callback registered; either a local
  159   			// (org.hibernate.Transaction) transaction has accepted
  160   			// callback responsibilities, or we have previously
  161   			// registered a transaction synch.
  162   			return true;
  163   		}
  164   		boolean localCallbacksOnly = owner.getFactory().getSettings()
  165   				.getTransactionFactory()
  166   				.areCallbacksLocalToHibernateTransactions();
  167   		if ( localCallbacksOnly ) {
  168   			// the configured transaction-factory says it only supports
  169   			// local callback mode, so no sense attempting to register a
  170   			// JTA Synchronization
  171   			return false;
  172   		}
  173   		TransactionManager tm = owner.getFactory().getTransactionManager();
  174   		if ( tm == null ) {
  175   			// if there is no TM configured, we will not be able to access
  176   			// the javax.transaction.Transaction object in order to
  177   			// register a synch anyway.
  178   			return false;
  179   		}
  180   		else {
  181   			try {
  182   				if ( !isTransactionInProgress() ) {
  183   					log.trace( "TransactionFactory reported no active transaction; Synchronization not registered" );
  184   					return false;
  185   				}
  186   				else {
  187   					javax.transaction.Transaction tx = tm.getTransaction();
  188   					if ( JTAHelper.isMarkedForRollback( tx ) ) {
  189   						// transactions marked for rollback-only cause some TM impls to throw exceptions
  190   						log.debug( "Transaction is marked for rollback; skipping Synchronization registration" );
  191   						return false;
  192   					}
  193   					else {
  194   						if ( hibernateTransaction == null ) {
  195   							hibernateTransaction = owner.getFactory().getSettings().getTransactionFactory().createTransaction( this, owner );
  196   						}
  197   						tx.registerSynchronization( new CacheSynchronization(owner, this, tx, hibernateTransaction) );
  198   						isTransactionCallbackRegistered = true;
  199   						log.debug("successfully registered Synchronization");
  200   						return true;
  201   					}
  202   				}
  203   			}
  204   			catch( HibernateException e ) {
  205   				throw e;
  206   			}
  207   			catch (Exception e) {
  208   				throw new TransactionException( "could not register synchronization with JTA TransactionManager", e );
  209   			}
  210   		}
  211   	}
  212   	
  213   	public boolean isTransactionInProgress() {
  214   		return owner.getFactory().getSettings().getTransactionFactory()
  215   				.isTransactionInProgress( this, owner, hibernateTransaction );
  216   	}
  217   
  218   	public Transaction getTransaction() throws HibernateException {
  219   		if (hibernateTransaction==null) {
  220   			hibernateTransaction = owner.getFactory().getSettings()
  221   					.getTransactionFactory()
  222   					.createTransaction( this, owner );
  223   		}
  224   		return hibernateTransaction;
  225   	}
  226   	
  227   	public void beforeTransactionCompletion(Transaction tx) {
  228   		log.trace( "before transaction completion" );
  229   		owner.beforeTransactionCompletion(tx);
  230   	}
  231   	
  232   	/**
  233   	 * We cannot rely upon this method being called! It is only
  234   	 * called if we are using Hibernate Transaction API.
  235   	 */
  236   	public void afterTransactionBegin(Transaction tx) {
  237   		log.trace( "after transaction begin" );
  238   		owner.afterTransactionBegin(tx);
  239   	}
  240   
  241   	public void afterTransactionCompletion(boolean success, Transaction tx) {
  242   		log.trace( "after transaction completion" );
  243   
  244   		if ( getFactory().getStatistics().isStatisticsEnabled() ) {
  245   			getFactory().getStatisticsImplementor().endTransaction(success);
  246   		}
  247   
  248   		connectionManager.afterTransaction();
  249   
  250   		isTransactionCallbackRegistered = false;
  251   		hibernateTransaction = null;
  252   		owner.afterTransactionCompletion(success, tx);
  253   	}
  254   	
  255   	/**
  256   	 * Called after executing a query outside the scope of
  257   	 * a Hibernate or JTA transaction
  258   	 */
  259   	public void afterNontransactionalQuery(boolean success) {
  260   		log.trace( "after autocommit" );
  261   		try {
  262   			// check to see if the connection is in auto-commit 
  263   			// mode (no connection means aggressive connection
  264   			// release outside a JTA transaction context, so MUST
  265   			// be autocommit mode)
  266   			boolean isAutocommit = connectionManager.isAutoCommit();
  267   
  268   			connectionManager.afterTransaction();
  269   			
  270   			if ( isAutocommit ) {
  271   				owner.afterTransactionCompletion(success, null);
  272   			}
  273   		}
  274   		catch (SQLException sqle) {
  275   			throw JDBCExceptionHelper.convert( 
  276   					owner.getFactory().getSQLExceptionConverter(),
  277   					sqle,
  278   					"could not inspect JDBC autocommit mode"
  279   				);
  280   		}
  281   	}
  282   
  283   
  284   	// serialization ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  285   
  286   	private void writeObject(ObjectOutputStream oos) throws IOException {
  287   		// isTransactionCallbackRegistered denotes whether any Hibernate
  288   		// Transaction has registered as a callback against this
  289   		// JDBCContext; only one such callback is allowed.  Directly
  290   		// serializing this value causes problems with JDBCTransaction,
  291   		// or really any Transaction impl where the callback is local
  292   		// to the Transaction instance itself, since that Transaction
  293   		// is not serialized along with the JDBCContext.  Thus we
  294   		// handle that fact here explicitly...
  295   		oos.defaultWriteObject();
  296   		boolean deserHasCallbackRegistered = isTransactionCallbackRegistered
  297   				&& ! owner.getFactory().getSettings().getTransactionFactory()
  298   				.areCallbacksLocalToHibernateTransactions();
  299   		oos.writeBoolean( deserHasCallbackRegistered );
  300   	}
  301   
  302   	private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
  303   		ois.defaultReadObject();
  304   		isTransactionCallbackRegistered = ois.readBoolean();
  305   	}
  306   
  307   	/**
  308   	 * Custom serialization routine used during serialization of a
  309   	 * Session/PersistenceContext for increased performance.
  310   	 *
  311   	 * @param oos The stream to which we should write the serial data.
  312   	 * @throws IOException
  313   	 */
  314   	public void serialize(ObjectOutputStream oos) throws IOException {
  315   		connectionManager.serialize( oos );
  316   	}
  317   
  318   	/**
  319   	 * Custom deserialization routine used during deserialization of a
  320   	 * Session/PersistenceContext for increased performance.
  321   	 *
  322   	 * @param ois The stream from which to read the entry.
  323   	 * @throws IOException
  324   	 */
  325   	public static JDBCContext deserialize(
  326   			ObjectInputStream ois,
  327   	        Context context,
  328   	        Interceptor interceptor) throws IOException {
  329   		JDBCContext jdbcContext = new JDBCContext();
  330   		jdbcContext.owner = context;
  331   		jdbcContext.connectionManager = ConnectionManager.deserialize(
  332   				ois,
  333   				context.getFactory(),
  334   		        interceptor,
  335   		        context.getConnectionReleaseMode(),
  336   		        jdbcContext
  337   		);
  338   		return jdbcContext;
  339   	}
  340   }

Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » jdbc » [javadoc | source]