All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha1
Show newest version
/*
 * 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 .
 */
package org.hibernate.engine.jdbc.internal;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.hibernate.ConnectionReleaseMode;
import org.hibernate.HibernateException;
import org.hibernate.TransactionException;
import org.hibernate.engine.jdbc.batch.spi.Batch;
import org.hibernate.engine.jdbc.batch.spi.BatchBuilder;
import org.hibernate.engine.jdbc.batch.spi.BatchKey;
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
import org.hibernate.engine.jdbc.spi.InvalidatableWrapper;
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.JdbcWrapper;
import org.hibernate.engine.jdbc.spi.ResultSetReturn;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
import org.hibernate.engine.jdbc.spi.StatementPreparer;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.jdbc.WorkExecutor;
import org.hibernate.jdbc.WorkExecutorVisitable;
import org.hibernate.resource.jdbc.ResourceRegistry;
import org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl;
import org.hibernate.resource.jdbc.internal.LogicalConnectionProvidedImpl;
import org.hibernate.resource.jdbc.internal.ResourceRegistryStandardImpl;
import org.hibernate.resource.jdbc.spi.JdbcSessionOwner;
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
import org.hibernate.resource.transaction.backend.jdbc.spi.JdbcResourceTransaction;

/**
 * Standard Hibernate implementation of {@link JdbcCoordinator}
 * 

* IMPL NOTE : Custom serialization handling! * * @author Steve Ebersole * @author Brett Meyer * @author Sanne Grinovero */ public class JdbcCoordinatorImpl implements JdbcCoordinator { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( JdbcCoordinatorImpl.class ); private transient LogicalConnectionImplementor logicalConnection; private transient JdbcSessionOwner owner; private transient JdbcServices jdbcServices; private transient Batch currentBatch; private transient long transactionTimeOutInstant = -1; private Statement lastQuery; private final boolean isUserSuppliedConnection; /** * If true, manually (and temporarily) circumvent aggressive release processing. */ private boolean releasesEnabled = true; /** * Constructs a JdbcCoordinatorImpl * * @param userSuppliedConnection The user supplied connection (may be null) */ public JdbcCoordinatorImpl( Connection userSuppliedConnection, JdbcSessionOwner owner, JdbcServices jdbcServices) { this.isUserSuppliedConnection = userSuppliedConnection != null; final ResourceRegistry resourceRegistry = new ResourceRegistryStandardImpl( owner.getJdbcSessionContext().getObserver() ); if ( isUserSuppliedConnection ) { this.logicalConnection = new LogicalConnectionProvidedImpl( userSuppliedConnection, resourceRegistry ); } else { this.logicalConnection = new LogicalConnectionManagedImpl( owner.getJdbcConnectionAccess(), owner.getJdbcSessionContext(), resourceRegistry, jdbcServices ); } this.owner = owner; this.jdbcServices = jdbcServices; } private JdbcCoordinatorImpl( LogicalConnectionImplementor logicalConnection, boolean isUserSuppliedConnection, JdbcSessionOwner owner) { this.logicalConnection = logicalConnection; this.isUserSuppliedConnection = isUserSuppliedConnection; this.owner = owner; this.jdbcServices = owner.getJdbcSessionContext() .getServiceRegistry() .getService( JdbcServices.class ); } @Override public LogicalConnectionImplementor getLogicalConnection() { return logicalConnection; } protected SessionFactoryImplementor sessionFactory() { return this.owner.getJdbcSessionContext().getSessionFactory(); } protected BatchBuilder batchBuilder() { return sessionFactory().getServiceRegistry().getService( BatchBuilder.class ); } /** * Access to the SqlExceptionHelper * * @return The SqlExceptionHelper */ public SqlExceptionHelper sqlExceptionHelper() { return jdbcServices.getSqlExceptionHelper(); } private int flushDepth; @Override public void flushBeginning() { if ( flushDepth == 0 ) { releasesEnabled = false; } flushDepth++; } @Override public void flushEnding() { flushDepth--; if ( flushDepth < 0 ) { throw new HibernateException( "Mismatched flush handling" ); } if ( flushDepth == 0 ) { releasesEnabled = true; } afterStatementExecution(); } @Override public Connection close() { LOG.tracev( "Closing JDBC container [{0}]", this ); Connection connection; try { if ( currentBatch != null ) { LOG.closingUnreleasedBatch(); currentBatch.release(); } } finally { connection = logicalConnection.close(); } return connection; } @Override public Batch getBatch(BatchKey key) { if ( currentBatch != null ) { if ( currentBatch.getKey().equals( key ) ) { return currentBatch; } else { currentBatch.execute(); currentBatch.release(); } } currentBatch = batchBuilder().buildBatch( key, this ); return currentBatch; } @Override public void executeBatch() { if ( currentBatch != null ) { currentBatch.execute(); // needed? currentBatch.release(); } } @Override public void abortBatch() { if ( currentBatch != null ) { currentBatch.release(); } } private transient StatementPreparer statementPreparer; @Override public StatementPreparer getStatementPreparer() { if ( statementPreparer == null ) { statementPreparer = new StatementPreparerImpl( this, jdbcServices ); } return statementPreparer; } private transient ResultSetReturn resultSetExtractor; @Override public ResultSetReturn getResultSetReturn() { if ( resultSetExtractor == null ) { resultSetExtractor = new ResultSetReturnImpl( this, jdbcServices ); } return resultSetExtractor; } @Override public void setTransactionTimeOut(int seconds) { transactionTimeOutInstant = System.currentTimeMillis() + ( seconds * 1000 ); } @Override public void flushBeforeTransactionCompletion() { getJdbcSessionOwner().flushBeforeTransactionCompletion(); } @Override public int determineRemainingTransactionTimeOutPeriod() { if ( transactionTimeOutInstant < 0 ) { return -1; } final int secondsRemaining = (int) ((transactionTimeOutInstant - System.currentTimeMillis()) / 1000); if ( secondsRemaining <= 0 ) { throw new TransactionException( "transaction timeout expired" ); } return secondsRemaining; } @Override public void afterStatementExecution() { LOG.tracev( "Starting after statement execution processing [{0}]", getConnectionReleaseMode() ); if ( getConnectionReleaseMode() == ConnectionReleaseMode.AFTER_STATEMENT ) { if ( ! releasesEnabled ) { LOG.debug( "Skipping aggressive release due to manual disabling" ); return; } if ( hasRegisteredResources() ) { LOG.debug( "Skipping aggressive release due to registered resources" ); return; } getLogicalConnection().afterStatement(); } } @Override public void afterTransaction() { transactionTimeOutInstant = -1; if ( getConnectionReleaseMode() == ConnectionReleaseMode.AFTER_STATEMENT || getConnectionReleaseMode() == ConnectionReleaseMode.AFTER_TRANSACTION ) { this.logicalConnection.afterTransaction(); } } private void releaseResources() { getResourceRegistry().releaseResources(); } private boolean hasRegisteredResources() { return getResourceRegistry().hasRegisteredResources(); } private ConnectionReleaseMode determineConnectionReleaseMode( JdbcConnectionAccess jdbcConnectionAccess, boolean isUserSuppliedConnection, ConnectionReleaseMode connectionReleaseMode) { if ( isUserSuppliedConnection ) { return ConnectionReleaseMode.ON_CLOSE; } else if ( connectionReleaseMode == ConnectionReleaseMode.AFTER_STATEMENT && ! jdbcConnectionAccess.supportsAggressiveRelease() ) { LOG.debug( "Connection provider reports to not support aggressive release; overriding" ); return ConnectionReleaseMode.AFTER_TRANSACTION; } else { return connectionReleaseMode; } } @Override public T coordinateWork(WorkExecutorVisitable work) { final Connection connection = getLogicalConnection().getPhysicalConnection(); try { final T result = work.accept( new WorkExecutor(), connection ); afterStatementExecution(); return result; } catch ( SQLException e ) { throw sqlExceptionHelper().convert( e, "error executing work" ); } } @Override public boolean isReadyForSerialization() { return this.isUserSuppliedConnection ? ! getLogicalConnection().isPhysicallyConnected() : ! hasRegisteredResources(); } @Override @SuppressWarnings({ "unchecked" }) public void registerLastQuery(Statement statement) { LOG.tracev( "Registering last query statement [{0}]", statement ); if ( statement instanceof JdbcWrapper ) { final JdbcWrapper wrapper = (JdbcWrapper) statement; registerLastQuery( wrapper.getWrappedObject() ); return; } lastQuery = statement; } @Override public void cancelLastQuery() { try { if ( lastQuery != null ) { lastQuery.cancel(); } } catch (SQLException sqle) { SqlExceptionHelper sqlExceptionHelper = jdbcServices.getSqlExceptionHelper(); //Should always be non-null, but to make sure as the implementation is lazy: if ( sqlExceptionHelper != null ) { sqlExceptionHelper = new SqlExceptionHelper( false ); } throw sqlExceptionHelper.convert( sqle, "Cannot cancel query" ); } finally { lastQuery = null; } } @Override public void enableReleases() { releasesEnabled = true; } @Override public void disableReleases() { releasesEnabled = false; } @SuppressWarnings({ "unchecked" }) protected void close(Statement statement) { LOG.tracev( "Closing prepared statement [{0}]", statement ); // Important for Statement caching -- some DBs (especially Sybase) log warnings on every Statement under // certain situations. sqlExceptionHelper().logAndClearWarnings( statement ); if ( statement instanceof InvalidatableWrapper ) { final InvalidatableWrapper wrapper = (InvalidatableWrapper) statement; close( wrapper.getWrappedObject() ); wrapper.invalidate(); return; } try { // if we are unable to "clean" the prepared statement, // we do not close it try { if ( statement.getMaxRows() != 0 ) { statement.setMaxRows( 0 ); } if ( statement.getQueryTimeout() != 0 ) { statement.setQueryTimeout( 0 ); } } catch( SQLException sqle ) { // there was a problem "cleaning" the prepared statement if ( LOG.isDebugEnabled() ) { LOG.debugf( "Exception clearing maxRows/queryTimeout [%s]", sqle.getMessage() ); } // EARLY EXIT!!! return; } statement.close(); if ( lastQuery == statement ) { lastQuery = null; } } catch( SQLException e ) { LOG.debugf( "Unable to release JDBC statement [%s]", e.getMessage() ); } catch ( Exception e ) { // try to handle general errors more elegantly LOG.debugf( "Unable to release JDBC statement [%s]", e.getMessage() ); } } @SuppressWarnings({ "unchecked" }) protected void close(ResultSet resultSet) { LOG.tracev( "Closing result set [{0}]", resultSet ); if ( resultSet instanceof InvalidatableWrapper ) { final InvalidatableWrapper wrapper = (InvalidatableWrapper) resultSet; close( wrapper.getWrappedObject() ); wrapper.invalidate(); return; } try { resultSet.close(); } catch( SQLException e ) { LOG.debugf( "Unable to release JDBC result set [%s]", e.getMessage() ); } catch ( Exception e ) { // try to handle general errors more elegantly LOG.debugf( "Unable to release JDBC result set [%s]", e.getMessage() ); } } @Override public boolean isActive() { return !sessionFactory().isClosed(); } @Override public void afterTransactionBegin() { owner.afterTransactionBegin(); } @Override public void beforeTransactionCompletion() { this.owner.beforeTransactionCompletion(); this.logicalConnection.beforeTransactionCompletion(); } @Override public void afterTransactionCompletion(boolean successful, boolean delayed) { afterTransaction(); owner.afterTransactionCompletion( successful, delayed ); } @Override public JdbcSessionOwner getJdbcSessionOwner() { return this.owner; } @Override public JdbcResourceTransaction getResourceLocalTransaction() { return logicalConnection.getPhysicalJdbcTransaction(); } /** * JDK serialization hook * * @param oos The stream into which to write our state * * @throws IOException Trouble accessing the stream */ @Override public void serialize(ObjectOutputStream oos) throws IOException { if ( ! isReadyForSerialization() ) { throw new HibernateException( "Cannot serialize Session while connected" ); } oos.writeBoolean( isUserSuppliedConnection ); logicalConnection.serialize( oos ); } /** * JDK deserialization hook * * @param ois The stream into which to write our state * @param owner The Jdbc Session owner which owns the JdbcCoordinatorImpl to be deserialized. * * @return The deserialized JdbcCoordinatorImpl * * @throws IOException Trouble accessing the stream * @throws ClassNotFoundException Trouble reading the stream */ public static JdbcCoordinatorImpl deserialize( ObjectInputStream ois, JdbcSessionOwner owner) throws IOException, ClassNotFoundException { final boolean isUserSuppliedConnection = ois.readBoolean(); LogicalConnectionImplementor logicalConnection; if ( isUserSuppliedConnection ) { logicalConnection = LogicalConnectionProvidedImpl.deserialize( ois ); } else { logicalConnection = LogicalConnectionManagedImpl.deserialize( ois, owner.getJdbcConnectionAccess(), owner.getJdbcSessionContext() ); } return new JdbcCoordinatorImpl( logicalConnection, isUserSuppliedConnection, owner ); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy