HHH-5765 : Integrate LogicalConnection into ConnectionManager

This commit is contained in:
Gail Badner 2010-12-02 11:16:10 -08:00
parent da1750881a
commit 97fef96b98
11 changed files with 303 additions and 271 deletions

View File

@ -36,6 +36,7 @@ import org.hibernate.Interceptor;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import org.hibernate.ConnectionReleaseMode; import org.hibernate.ConnectionReleaseMode;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.SQLExceptionHelper; import org.hibernate.engine.jdbc.spi.SQLExceptionHelper;
import org.hibernate.proxy.EntityNotFoundDelegate; import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.engine.query.QueryPlanCache; import org.hibernate.engine.query.QueryPlanCache;
@ -97,10 +98,16 @@ public interface SessionFactoryImplementor extends Mapping, SessionFactory {
*/ */
public CollectionPersister getCollectionPersister(String role) throws MappingException; public CollectionPersister getCollectionPersister(String role) throws MappingException;
/**
* Get the JdbcServices.
* @return the JdbcServices
*/
public JdbcServices getJdbcServices();
/** /**
* Get the SQL dialect. * Get the SQL dialect.
* <p/> * <p/>
* Shorthand for {@link #getSettings()}.{@link Settings#getDialect()} * Shorthand for {@link #getJdbcServices().getDialect()}.{@link JdbcServices#getDialect()}
* *
* @return The dialect * @return The dialect
*/ */

View File

@ -23,6 +23,9 @@
*/ */
package org.hibernate.engine.jdbc.internal; 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.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
@ -38,6 +41,7 @@ import org.hibernate.engine.jdbc.spi.JdbcResourceRegistry;
import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.ConnectionObserver; import org.hibernate.engine.jdbc.spi.ConnectionObserver;
import org.hibernate.engine.jdbc.spi.LogicalConnectionImplementor; import org.hibernate.engine.jdbc.spi.LogicalConnectionImplementor;
import org.hibernate.jdbc.BorrowedConnectionProxy;
/** /**
* LogicalConnectionImpl implementation * LogicalConnectionImpl implementation
@ -48,6 +52,8 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor {
private static final Logger log = LoggerFactory.getLogger( LogicalConnectionImpl.class ); private static final Logger log = LoggerFactory.getLogger( LogicalConnectionImpl.class );
private transient Connection physicalConnection; private transient Connection physicalConnection;
private transient Connection borrowedConnection;
private final ConnectionReleaseMode connectionReleaseMode; private final ConnectionReleaseMode connectionReleaseMode;
private final JdbcServices jdbcServices; private final JdbcServices jdbcServices;
private final JdbcResourceRegistry jdbcResourceRegistry; private final JdbcResourceRegistry jdbcResourceRegistry;
@ -61,19 +67,47 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor {
Connection userSuppliedConnection, Connection userSuppliedConnection,
ConnectionReleaseMode connectionReleaseMode, ConnectionReleaseMode connectionReleaseMode,
JdbcServices jdbcServices) { JdbcServices jdbcServices) {
if ( connectionReleaseMode == ConnectionReleaseMode.AFTER_STATEMENT &&
! jdbcServices.getConnectionProvider().supportsAggressiveRelease() ) {
log.debug( "connection provider reports to not support aggressive release; overriding" );
connectionReleaseMode = ConnectionReleaseMode.AFTER_TRANSACTION;
}
this.physicalConnection = userSuppliedConnection; this.physicalConnection = userSuppliedConnection;
this.connectionReleaseMode = connectionReleaseMode; this.connectionReleaseMode =
determineConnectionReleaseMode(
userSuppliedConnection != null, connectionReleaseMode, jdbcServices
);
this.jdbcServices = jdbcServices; this.jdbcServices = jdbcServices;
this.jdbcResourceRegistry = new JdbcResourceRegistryImpl( jdbcServices.getSqlExceptionHelper() ); this.jdbcResourceRegistry = new JdbcResourceRegistryImpl( jdbcServices.getSqlExceptionHelper() );
this.isUserSuppliedConnection = ( userSuppliedConnection != null ); this.isUserSuppliedConnection = ( userSuppliedConnection != null );
} }
public LogicalConnectionImpl(
ConnectionReleaseMode connectionReleaseMode,
JdbcServices jdbcServices,
boolean isUserSuppliedConnection,
boolean isClosed) {
this.connectionReleaseMode = determineConnectionReleaseMode(
isUserSuppliedConnection, connectionReleaseMode, jdbcServices
);
this.jdbcServices = jdbcServices;
this.jdbcResourceRegistry = new JdbcResourceRegistryImpl( jdbcServices.getSqlExceptionHelper() );
this.isUserSuppliedConnection = isUserSuppliedConnection;
this.isClosed = isClosed;
}
private static ConnectionReleaseMode determineConnectionReleaseMode(boolean isUserSuppliedConnection,
ConnectionReleaseMode connectionReleaseMode,
JdbcServices jdbcServices) {
if ( isUserSuppliedConnection ) {
return ConnectionReleaseMode.ON_CLOSE;
}
else if ( connectionReleaseMode == ConnectionReleaseMode.AFTER_STATEMENT &&
! jdbcServices.getConnectionProvider().supportsAggressiveRelease() ) {
log.debug( "connection provider reports to not support aggressive release; overriding" );
return ConnectionReleaseMode.AFTER_TRANSACTION;
}
else {
return connectionReleaseMode;
}
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@ -130,20 +164,25 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor {
* {@inheritDoc} * {@inheritDoc}
*/ */
public Connection close() { public Connection close() {
log.trace( "closing logical connection" );
Connection c = physicalConnection; Connection c = physicalConnection;
if ( !isUserSuppliedConnection && physicalConnection != null ) { try {
jdbcResourceRegistry.close(); releaseBorrowedConnection();
releaseConnection(); log.trace( "closing logical connection" );
if ( !isUserSuppliedConnection && physicalConnection != null ) {
jdbcResourceRegistry.close();
releaseConnection();
}
return c;
} }
// not matter what finally {
physicalConnection = null; // no matter what
isClosed = true; physicalConnection = null;
for ( ConnectionObserver observer : observers ) { isClosed = true;
observer.logicalConnectionClosed(); log.trace( "logical connection closed" );
} for ( ConnectionObserver observer : observers ) {
log.trace( "logical connection closed" ); observer.logicalConnectionClosed();
return c; }
}
} }
/** /**
@ -153,6 +192,40 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor {
return connectionReleaseMode; return connectionReleaseMode;
} }
public boolean isUserSuppliedConnection() {
return isUserSuppliedConnection;
}
public boolean hasBorrowedConnection() {
return borrowedConnection != null;
}
public Connection borrowConnection() {
if ( isClosed ) {
throw new HibernateException( "connection has been closed" );
}
if ( isUserSuppliedConnection() ) {
return physicalConnection;
}
else {
if ( borrowedConnection == null ) {
borrowedConnection = BorrowedConnectionProxy.generateProxy( this );
}
return borrowedConnection;
}
}
public void releaseBorrowedConnection() {
if ( borrowedConnection != null ) {
try {
BorrowedConnectionProxy.renderUnuseable( borrowedConnection );
}
finally {
borrowedConnection = null;
}
}
}
public void afterStatementExecution() { public void afterStatementExecution() {
log.trace( "starting after statement execution processing [{}]", connectionReleaseMode ); log.trace( "starting after statement execution processing [{}]", connectionReleaseMode );
if ( connectionReleaseMode == ConnectionReleaseMode.AFTER_STATEMENT ) { if ( connectionReleaseMode == ConnectionReleaseMode.AFTER_STATEMENT ) {
@ -187,7 +260,8 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor {
public void enableReleases() { public void enableReleases() {
log.trace( "(re)enabling releases" ); log.trace( "(re)enabling releases" );
releasesEnabled = true; releasesEnabled = true;
afterStatementExecution(); //FIXME: uncomment after new batch stuff is integrated!!!
//afterStatementExecution();
} }
/** /**
@ -232,19 +306,101 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor {
*/ */
private void releaseConnection() throws JDBCException { private void releaseConnection() throws JDBCException {
log.debug( "releasing JDBC connection" ); log.debug( "releasing JDBC connection" );
if ( physicalConnection == null ) {
return;
}
try { try {
if ( !physicalConnection.isClosed() ) { if ( ! physicalConnection.isClosed() ) {
getJdbcServices().getSqlExceptionHelper().logAndClearWarnings( physicalConnection ); getJdbcServices().getSqlExceptionHelper().logAndClearWarnings( physicalConnection );
} }
for ( ConnectionObserver observer : observers ) { if ( !isUserSuppliedConnection ) {
observer.physicalConnectionReleased(); getJdbcServices().getConnectionProvider().closeConnection( physicalConnection );
} }
getJdbcServices().getConnectionProvider().closeConnection( physicalConnection );
physicalConnection = null;
log.debug( "released JDBC connection" ); log.debug( "released JDBC connection" );
} }
catch (SQLException sqle) { catch (SQLException sqle) {
throw getJdbcServices().getSqlExceptionHelper().convert( sqle, "Could not close connection" ); throw getJdbcServices().getSqlExceptionHelper().convert( sqle, "Could not close connection" );
} }
finally {
physicalConnection = null;
}
log.debug( "released JDBC connection" );
for ( ConnectionObserver observer : observers ) {
observer.physicalConnectionReleased();
}
} }
/**
* Manually disconnect the underlying JDBC Connection. The assumption here
* is that the manager will be reconnected at a later point in time.
*
* @return The connection mantained here at time of disconnect. Null if
* there was no connection cached internally.
*/
public Connection manualDisconnect() {
if ( isClosed ) {
throw new IllegalStateException( "cannot manually disconnect because logical connection is already closed" );
}
Connection c = physicalConnection;
releaseConnection();
return c;
}
/**
* Manually reconnect the underlying JDBC Connection. Should be called at
* some point after manualDisconnect().
* <p/>
* This form is used for user-supplied connections.
*/
public void reconnect(Connection suppliedConnection) {
if ( isClosed ) {
throw new IllegalStateException( "cannot manually reconnect because logical connection is already closed" );
}
if ( isUserSuppliedConnection ) {
if ( suppliedConnection == null ) {
throw new IllegalArgumentException( "cannot reconnect a null user-supplied connection" );
}
else if ( suppliedConnection == physicalConnection ) {
log.warn( "reconnecting the same connection that is already connected; should this connection have been disconnected?" );
}
else if ( physicalConnection != null ) {
throw new IllegalArgumentException(
"cannot reconnect to a new user-supplied connection because currently connected; must disconnect before reconnecting."
);
}
physicalConnection = suppliedConnection;
log.debug( "reconnected JDBC connection" );
}
else {
if ( suppliedConnection != null ) {
throw new IllegalStateException( "unexpected user-supplied connection" );
}
log.debug( "called reconnect() with null connection (not user-supplied)" );
}
}
public boolean isReadyForSerialization() {
return isUserSuppliedConnection() ?
! isPhysicallyConnected() :
! getResourceRegistry().hasRegisteredResources()
;
}
public void serialize(ObjectOutputStream oos) throws IOException {
oos.writeBoolean( isUserSuppliedConnection );
oos.writeBoolean( isClosed );
}
public static LogicalConnectionImpl deserialize(
ObjectInputStream ois,
JdbcServices jdbcServices,
ConnectionReleaseMode connectionReleaseMode ) throws IOException {
return new LogicalConnectionImpl(
connectionReleaseMode,
jdbcServices,
ois.readBoolean(),
ois.readBoolean()
);
}
} }

View File

@ -23,6 +23,7 @@
*/ */
package org.hibernate.engine.jdbc.spi; package org.hibernate.engine.jdbc.spi;
import java.io.Serializable;
import java.sql.Connection; import java.sql.Connection;
/** /**
@ -30,7 +31,7 @@ import java.sql.Connection;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface LogicalConnection { public interface LogicalConnection extends Serializable {
/** /**
* Is this logical connection open? Another phraseology sometimes used is: "are we * Is this logical connection open? Another phraseology sometimes used is: "are we
* logically connected"? * logically connected"?

View File

@ -711,11 +711,15 @@ public final class SessionFactoryImpl implements SessionFactory, SessionFactoryI
return settings; return settings;
} }
public JdbcServices getJdbcServices() {
return serviceRegistry.getService( JdbcServices.class );
}
public Dialect getDialect() { public Dialect getDialect() {
if ( serviceRegistry == null ) { if ( serviceRegistry == null ) {
throw new IllegalStateException( "Cannot determine dialect because serviceRegistry is null." ); throw new IllegalStateException( "Cannot determine dialect because serviceRegistry is null." );
} }
return serviceRegistry.getService( JdbcServices.class ).getDialect(); return getJdbcServices().getDialect();
} }
public Interceptor getInterceptor() public Interceptor getInterceptor()
@ -910,10 +914,6 @@ public final class SessionFactoryImpl implements SessionFactory, SessionFactoryI
return getEntityPersister(className).getPropertyType(propertyName); return getEntityPersister(className).getPropertyType(propertyName);
} }
private JdbcServices getJdbcServices() {
return serviceRegistry.getService( JdbcServices.class );
}
public ConnectionProvider getConnectionProvider() { public ConnectionProvider getConnectionProvider() {
return serviceRegistry.getService( JdbcServices.class ).getConnectionProvider(); return serviceRegistry.getService( JdbcServices.class ).getConnectionProvider();
} }

View File

@ -43,6 +43,7 @@ import org.hibernate.ScrollMode;
import org.hibernate.TransactionException; import org.hibernate.TransactionException;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.jdbc.internal.LogicalConnectionImpl;
import org.hibernate.exception.JDBCExceptionHelper; import org.hibernate.exception.JDBCExceptionHelper;
import org.hibernate.jdbc.util.FormatStyle; import org.hibernate.jdbc.util.FormatStyle;
import org.hibernate.util.JDBCExceptionReporter; import org.hibernate.util.JDBCExceptionReporter;
@ -580,41 +581,6 @@ public abstract class AbstractBatcher implements Batcher {
} }
} }
public Connection openConnection() throws HibernateException {
log.debug("opening JDBC connection");
try {
return factory.getConnectionProvider().getConnection();
}
catch (SQLException sqle) {
throw factory.getSQLExceptionHelper().convert(
sqle,
"Cannot open connection"
);
}
}
public void closeConnection(Connection conn) throws HibernateException {
if ( conn == null ) {
log.debug( "found null connection on AbstractBatcher#closeConnection" );
// EARLY EXIT!!!!
return;
}
if ( log.isDebugEnabled() ) {
log.debug( "closing JDBC connection" + preparedStatementCountsToString() + resultSetCountsToString() );
}
try {
if ( !conn.isClosed() ) {
JDBCExceptionReporter.logAndClearWarnings( conn );
}
factory.getConnectionProvider().closeConnection( conn );
}
catch ( SQLException sqle ) {
throw factory.getSQLExceptionHelper().convert( sqle, "Cannot close connection" );
}
}
public void cancelLastQuery() throws HibernateException { public void cancelLastQuery() throws HibernateException {
try { try {
if (lastQuery!=null) lastQuery.cancel(); if (lastQuery!=null) lastQuery.cancel();

View File

@ -161,21 +161,6 @@ public interface Batcher {
public String openResourceStatsAsString(); public String openResourceStatsAsString();
// TODO : remove these last two as batcher is no longer managing connections
/**
* Obtain a JDBC connection
*
* @deprecated Obtain connections from {@link ConnectionProvider} instead
*/
public Connection openConnection() throws HibernateException;
/**
* Dispose of the JDBC connection
*
* @deprecated Obtain connections from {@link ConnectionProvider} instead
*/
public void closeConnection(Connection conn) throws HibernateException;
/** /**
* Set the transaction timeout to <tt>seconds</tt> later * Set the transaction timeout to <tt>seconds</tt> later
* than the current system time. * than the current system time.

View File

@ -29,6 +29,8 @@ import java.sql.SQLException;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.Interceptor; import org.hibernate.Interceptor;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.jdbc.internal.LogicalConnectionImpl;
/** /**
* An implementation of the <tt>Batcher</tt> interface that * An implementation of the <tt>Batcher</tt> interface that

View File

@ -25,6 +25,7 @@
package org.hibernate.jdbc; package org.hibernate.jdbc;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.engine.jdbc.internal.LogicalConnectionImpl;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@ -46,11 +47,11 @@ public class BorrowedConnectionProxy implements InvocationHandler {
private static final Class[] PROXY_INTERFACES = new Class[] { Connection.class, ConnectionWrapper.class }; private static final Class[] PROXY_INTERFACES = new Class[] { Connection.class, ConnectionWrapper.class };
private final ConnectionManager connectionManager; private final LogicalConnectionImpl logicalConnection;
private boolean useable = true; private boolean useable = true;
public BorrowedConnectionProxy(ConnectionManager connectionManager) { public BorrowedConnectionProxy(LogicalConnectionImpl logicalConnection) {
this.connectionManager = connectionManager; this.logicalConnection = logicalConnection;
} }
/** /**
@ -58,7 +59,7 @@ public class BorrowedConnectionProxy implements InvocationHandler {
*/ */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ( "close".equals( method.getName() ) ) { if ( "close".equals( method.getName() ) ) {
connectionManager.releaseBorrowedConnection(); logicalConnection.releaseBorrowedConnection();
return null; return null;
} }
// should probably no-op commit/rollback here, at least in JTA scenarios // should probably no-op commit/rollback here, at least in JTA scenarios
@ -67,11 +68,11 @@ public class BorrowedConnectionProxy implements InvocationHandler {
} }
if ( "getWrappedConnection".equals( method.getName() ) ) { if ( "getWrappedConnection".equals( method.getName() ) ) {
return connectionManager.getConnection(); return logicalConnection.getConnection();
} }
try { try {
return method.invoke( connectionManager.getConnection(), args ); return method.invoke( logicalConnection.getConnection(), args );
} }
catch( InvocationTargetException e ) { catch( InvocationTargetException e ) {
throw e.getTargetException(); throw e.getTargetException();
@ -82,12 +83,12 @@ public class BorrowedConnectionProxy implements InvocationHandler {
* Generates a Connection proxy wrapping the connection managed by the passed * Generates a Connection proxy wrapping the connection managed by the passed
* connection manager. * connection manager.
* *
* @param connectionManager The connection manager to wrap with the * @param logicalConnection The logical connection to wrap with the
* connection proxy. * connection proxy.
* @return The generated proxy. * @return The generated proxy.
*/ */
public static Connection generateProxy(ConnectionManager connectionManager) { public static Connection generateProxy(LogicalConnectionImpl logicalConnection) {
BorrowedConnectionProxy handler = new BorrowedConnectionProxy( connectionManager ); BorrowedConnectionProxy handler = new BorrowedConnectionProxy( logicalConnection );
return ( Connection ) Proxy.newProxyInstance( return ( Connection ) Proxy.newProxyInstance(
getProxyClassLoader(), getProxyClassLoader(),
PROXY_INTERFACES, PROXY_INTERFACES,

View File

@ -37,8 +37,8 @@ import org.hibernate.ConnectionReleaseMode;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.Interceptor; import org.hibernate.Interceptor;
import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.jdbc.internal.LogicalConnectionImpl;
import org.hibernate.engine.jdbc.spi.ConnectionObserver; import org.hibernate.engine.jdbc.spi.ConnectionObserver;
import org.hibernate.util.JDBCExceptionReporter;
/** /**
* Encapsulates JDBC Connection management logic needed by Hibernate. * Encapsulates JDBC Connection management logic needed by Hibernate.
@ -60,16 +60,11 @@ public class ConnectionManager implements Serializable {
private transient SessionFactoryImplementor factory; private transient SessionFactoryImplementor factory;
private final Callback callback; private final Callback callback;
private final ConnectionReleaseMode releaseMode; private transient LogicalConnectionImpl connection;
private transient Connection connection;
private transient Connection borrowedConnection;
private final boolean wasConnectionSupplied;
private transient Batcher batcher; private transient Batcher batcher;
private transient Interceptor interceptor; private transient Interceptor interceptor;
private boolean isClosed;
private transient boolean isFlushing;
/** /**
* Constructs a ConnectionManager. * Constructs a ConnectionManager.
* <p/> * <p/>
@ -92,31 +87,35 @@ public class ConnectionManager implements Serializable {
this.interceptor = interceptor; this.interceptor = interceptor;
this.batcher = factory.getSettings().getBatcherFactory().createBatcher( this, interceptor ); this.batcher = factory.getSettings().getBatcherFactory().createBatcher( this, interceptor );
this.connection = connection; setupConnection( connection, releaseMode );
wasConnectionSupplied = ( connection != null );
this.releaseMode = wasConnectionSupplied ? ConnectionReleaseMode.ON_CLOSE : releaseMode;
} }
/** /**
* Private constructor used exclusively from custom serialization * Private constructor used exclusively from custom serialization
*/ */
private ConnectionManager( private ConnectionManager(
SessionFactoryImplementor factory, SessionFactoryImplementor factory,
Callback callback, Callback callback,
ConnectionReleaseMode releaseMode, ConnectionReleaseMode releaseMode,
Interceptor interceptor, Interceptor interceptor
boolean wasConnectionSupplied, ) {
boolean isClosed) {
this.factory = factory; this.factory = factory;
this.callback = callback; this.callback = callback;
this.interceptor = interceptor; this.interceptor = interceptor;
this.batcher = factory.getSettings().getBatcherFactory().createBatcher( this, interceptor ); this.batcher = factory.getSettings().getBatcherFactory().createBatcher( this, interceptor );
}
this.wasConnectionSupplied = wasConnectionSupplied; private void setupConnection(Connection suppliedConnection,
this.isClosed = isClosed; ConnectionReleaseMode releaseMode
this.releaseMode = wasConnectionSupplied ? ConnectionReleaseMode.ON_CLOSE : releaseMode; ) {
connection =
new LogicalConnectionImpl(
suppliedConnection,
releaseMode,
factory.getJdbcServices()
);
connection.addObserver( callback );
} }
/** /**
@ -137,15 +136,6 @@ public class ConnectionManager implements Serializable {
return batcher; return batcher;
} }
/**
* Was the connection being used here supplied by the user?
*
* @return True if the user supplied the JDBC connection; false otherwise
*/
public boolean isSuppliedConnection() {
return wasConnectionSupplied;
}
/** /**
* Retrieves the connection currently managed by this ConnectionManager. * Retrieves the connection currently managed by this ConnectionManager.
* <p/> * <p/>
@ -159,44 +149,20 @@ public class ConnectionManager implements Serializable {
* available (we are currently manually disconnected). * available (we are currently manually disconnected).
*/ */
public Connection getConnection() throws HibernateException { public Connection getConnection() throws HibernateException {
if ( isClosed ) { return connection.getConnection();
throw new HibernateException( "connection manager has been closed" );
}
if ( connection == null ) {
openConnection();
}
return connection;
} }
public boolean hasBorrowedConnection() { public boolean hasBorrowedConnection() {
// used from testsuite // used from testsuite
return borrowedConnection != null; return connection.hasBorrowedConnection();
} }
public Connection borrowConnection() { public Connection borrowConnection() {
if ( isClosed ) { return connection.borrowConnection();
throw new HibernateException( "connection manager has been closed" );
}
if ( isSuppliedConnection() ) {
return connection;
}
else {
if ( borrowedConnection == null ) {
borrowedConnection = BorrowedConnectionProxy.generateProxy( this );
}
return borrowedConnection;
}
} }
public void releaseBorrowedConnection() { public void releaseBorrowedConnection() {
if ( borrowedConnection != null ) { connection.releaseBorrowedConnection();
try {
BorrowedConnectionProxy.renderUnuseable( borrowedConnection );
}
finally {
borrowedConnection = null;
}
}
} }
/** /**
@ -208,9 +174,10 @@ public class ConnectionManager implements Serializable {
* @throws SQLException Can be thrown by the Connection.isAutoCommit() check. * @throws SQLException Can be thrown by the Connection.isAutoCommit() check.
*/ */
public boolean isAutoCommit() throws SQLException { public boolean isAutoCommit() throws SQLException {
return connection == null return connection == null ||
|| connection.isClosed() ! connection.isOpen() ||
|| connection.getAutoCommit(); ! connection.isPhysicallyConnected() ||
connection.getConnection().getAutoCommit();
} }
/** /**
@ -227,10 +194,10 @@ public class ConnectionManager implements Serializable {
* @return True if the connections will be released after each statement; false otherwise. * @return True if the connections will be released after each statement; false otherwise.
*/ */
public boolean isAggressiveRelease() { public boolean isAggressiveRelease() {
if ( releaseMode == ConnectionReleaseMode.AFTER_STATEMENT ) { if ( connection.getConnectionReleaseMode() == ConnectionReleaseMode.AFTER_STATEMENT ) {
return true; return true;
} }
else if ( releaseMode == ConnectionReleaseMode.AFTER_TRANSACTION ) { else if ( connection.getConnectionReleaseMode() == ConnectionReleaseMode.AFTER_TRANSACTION ) {
boolean inAutoCommitState; boolean inAutoCommitState;
try { try {
inAutoCommitState = isAutoCommit() && ! callback.isTransactionInProgress(); inAutoCommitState = isAutoCommit() && ! callback.isTransactionInProgress();
@ -256,7 +223,7 @@ public class ConnectionManager implements Serializable {
* @return True if the connections will be released after each statement; false otherwise. * @return True if the connections will be released after each statement; false otherwise.
*/ */
private boolean isAggressiveReleaseNoTransactionCheck() { private boolean isAggressiveReleaseNoTransactionCheck() {
if ( releaseMode == ConnectionReleaseMode.AFTER_STATEMENT ) { if ( connection.getConnectionReleaseMode() == ConnectionReleaseMode.AFTER_STATEMENT ) {
return true; return true;
} }
else { else {
@ -268,7 +235,7 @@ public class ConnectionManager implements Serializable {
// assume we are in an auto-commit state // assume we are in an auto-commit state
inAutoCommitState = true; inAutoCommitState = true;
} }
return releaseMode == ConnectionReleaseMode.AFTER_TRANSACTION && inAutoCommitState; return connection.getConnectionReleaseMode() == ConnectionReleaseMode.AFTER_TRANSACTION && inAutoCommitState;
} }
} }
@ -280,7 +247,17 @@ public class ConnectionManager implements Serializable {
* @return True if logically connected; false otherwise. * @return True if logically connected; false otherwise.
*/ */
public boolean isCurrentlyConnected() { public boolean isCurrentlyConnected() {
return wasConnectionSupplied ? connection != null : !isClosed; if ( connection != null ) {
if ( connection.isUserSuppliedConnection() ) {
return connection.isPhysicallyConnected();
}
else {
return connection.isOpen();
}
}
else {
return false;
}
} }
/** /**
@ -290,17 +267,11 @@ public class ConnectionManager implements Serializable {
*/ */
public void afterStatement() { public void afterStatement() {
if ( isAggressiveRelease() ) { if ( isAggressiveRelease() ) {
if ( isFlushing ) { if ( batcher.hasOpenResources() ) {
log.debug( "skipping aggressive-release due to flush cycle" );
}
else if ( batcher.hasOpenResources() ) {
log.debug( "skipping aggresive-release due to open resources on batcher" ); log.debug( "skipping aggresive-release due to open resources on batcher" );
} }
else if ( borrowedConnection != null ) {
log.debug( "skipping aggresive-release due to borrowed connection" );
}
else { else {
aggressiveRelease(); connection.afterStatementExecution();
} }
} }
} }
@ -311,27 +282,33 @@ public class ConnectionManager implements Serializable {
* indicates. * indicates.
*/ */
public void afterTransaction() { public void afterTransaction() {
if ( isAfterTransactionRelease() ) { if ( connection != null ) {
aggressiveRelease(); if ( isAfterTransactionRelease() ) {
} connection.afterTransaction();
else if ( isAggressiveReleaseNoTransactionCheck() && batcher.hasOpenResources() ) { }
log.info( "forcing batcher resource cleanup on transaction completion; forgot to close ScrollableResults/Iterator?" ); else if ( isAggressiveReleaseNoTransactionCheck() && batcher.hasOpenResources() ) {
batcher.closeStatements(); log.info( "forcing batcher resource cleanup on transaction completion; forgot to close ScrollableResults/Iterator?" );
aggressiveRelease(); batcher.closeStatements();
} connection.afterTransaction();
else if ( isOnCloseRelease() ) { }
// log a message about potential connection leaks else if ( isOnCloseRelease() ) {
log.debug( "transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!" ); // log a message about potential connection leaks
log.debug( "transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!" );
}
} }
batcher.unsetTransactionTimeout(); batcher.unsetTransactionTimeout();
} }
private boolean isAfterTransactionRelease() { private boolean isAfterTransactionRelease() {
return releaseMode == ConnectionReleaseMode.AFTER_TRANSACTION; return connection.getConnectionReleaseMode() == ConnectionReleaseMode.AFTER_TRANSACTION;
} }
private boolean isOnCloseRelease() { private boolean isOnCloseRelease() {
return releaseMode == ConnectionReleaseMode.ON_CLOSE; return connection.getConnectionReleaseMode() == ConnectionReleaseMode.ON_CLOSE;
}
public boolean isLogicallyConnected() {
return connection != null && connection.isOpen();
} }
/** /**
@ -342,12 +319,7 @@ public class ConnectionManager implements Serializable {
* there was no connection cached internally. * there was no connection cached internally.
*/ */
public Connection close() { public Connection close() {
try { return cleanup();
return cleanup();
}
finally {
isClosed = true;
}
} }
/** /**
@ -358,7 +330,11 @@ public class ConnectionManager implements Serializable {
* there was no connection cached internally. * there was no connection cached internally.
*/ */
public Connection manualDisconnect() { public Connection manualDisconnect() {
return cleanup(); if ( ! isLogicallyConnected() ) {
throw new IllegalStateException( "cannot manually disconnect because not logically connected." );
}
batcher.closeStatements();
return connection.manualDisconnect();
} }
/** /**
@ -368,6 +344,7 @@ public class ConnectionManager implements Serializable {
* This form is used for ConnectionProvider-supplied connections. * This form is used for ConnectionProvider-supplied connections.
*/ */
public void manualReconnect() { public void manualReconnect() {
manualReconnect( null );
} }
/** /**
@ -377,7 +354,10 @@ public class ConnectionManager implements Serializable {
* This form is used for user-supplied connections. * This form is used for user-supplied connections.
*/ */
public void manualReconnect(Connection suppliedConnection) { public void manualReconnect(Connection suppliedConnection) {
this.connection = suppliedConnection; if ( ! isLogicallyConnected() ) {
throw new IllegalStateException( "cannot manually disconnect because not logically connected." );
}
connection.reconnect( suppliedConnection );
} }
/** /**
@ -391,93 +371,22 @@ public class ConnectionManager implements Serializable {
* @throws HibernateException * @throws HibernateException
*/ */
private Connection cleanup() throws HibernateException { private Connection cleanup() throws HibernateException {
releaseBorrowedConnection();
if ( connection == null ) { if ( connection == null ) {
log.trace( "connection already null in cleanup : no action"); log.trace( "connection already null in cleanup : no action");
return null; return null;
} }
try { try {
log.trace( "performing cleanup" ); log.trace( "performing cleanup" );
batcher.closeStatements(); if ( isLogicallyConnected() ) {
Connection c = null; batcher.closeStatements();
if ( !wasConnectionSupplied ) {
closeConnection();
} }
else { Connection c = connection.close();
c = connection;
}
connection = null;
return c; return c;
} }
finally { finally {
callback.physicalConnectionReleased();
}
}
/**
* Performs actions required to perform an aggressive release of the
* JDBC Connection.
*/
private void aggressiveRelease() {
if ( !wasConnectionSupplied ) {
log.debug( "aggressively releasing JDBC connection" );
if ( connection != null ) {
closeConnection();
}
}
}
/**
* Pysically opens a JDBC Connection.
*
* @throws HibernateException
*/
private void openConnection() throws HibernateException {
if ( connection != null ) {
return;
}
log.debug("opening JDBC connection");
try {
connection = factory.getConnectionProvider().getConnection();
}
catch (SQLException sqle) {
throw factory.getSQLExceptionHelper().convert(
sqle,
"Cannot open connection"
);
}
callback.physicalConnectionObtained( connection ); // register synch; stats.connect()
}
/**
* Physically closes the JDBC Connection.
*/
private void closeConnection() {
if ( log.isDebugEnabled() ) {
log.debug(
"releasing JDBC connection [" +
batcher.openResourceStatsAsString() + "]"
);
}
try {
if ( !connection.isClosed() ) {
JDBCExceptionReporter.logAndClearWarnings( connection );
}
factory.getConnectionProvider().closeConnection( connection );
connection = null; connection = null;
} }
catch (SQLException sqle) {
throw factory.getSQLExceptionHelper().convert(
sqle,
"Cannot release connection"
);
}
} }
/** /**
@ -487,7 +396,7 @@ public class ConnectionManager implements Serializable {
*/ */
public void flushBeginning() { public void flushBeginning() {
log.trace( "registering flush begin" ); log.trace( "registering flush begin" );
isFlushing = true; connection.disableReleases();
} }
/** /**
@ -496,12 +405,12 @@ public class ConnectionManager implements Serializable {
*/ */
public void flushEnding() { public void flushEnding() {
log.trace( "registering flush end" ); log.trace( "registering flush end" );
isFlushing = false; connection.enableReleases();
afterStatement(); afterStatement();
} }
public boolean isReadyForSerialization() { public boolean isReadyForSerialization() {
return wasConnectionSupplied ? connection == null : !batcher.hasOpenResources(); return connection == null ? true : ! batcher.hasOpenResources() && connection.isReadyForSerialization();
} }
/** /**
@ -531,13 +440,10 @@ public class ConnectionManager implements Serializable {
factory = (SessionFactoryImplementor) ois.readObject(); factory = (SessionFactoryImplementor) ois.readObject();
interceptor = (Interceptor) ois.readObject(); interceptor = (Interceptor) ois.readObject();
ois.defaultReadObject(); ois.defaultReadObject();
this.batcher = factory.getSettings().getBatcherFactory().createBatcher( this, interceptor );
} }
public void serialize(ObjectOutputStream oos) throws IOException { public void serialize(ObjectOutputStream oos) throws IOException {
oos.writeBoolean( wasConnectionSupplied ); connection.serialize( oos );
oos.writeBoolean( isClosed );
} }
public static ConnectionManager deserialize( public static ConnectionManager deserialize(
@ -546,14 +452,19 @@ public class ConnectionManager implements Serializable {
Interceptor interceptor, Interceptor interceptor,
ConnectionReleaseMode connectionReleaseMode, ConnectionReleaseMode connectionReleaseMode,
Callback callback) throws IOException { Callback callback) throws IOException {
return new ConnectionManager( ConnectionManager connectionManager = new ConnectionManager(
factory, factory,
callback, callback,
connectionReleaseMode, connectionReleaseMode,
interceptor, interceptor
ois.readBoolean(),
ois.readBoolean()
); );
connectionManager.connection =
LogicalConnectionImpl.deserialize(
ois,
factory.getJdbcServices( ),
connectionReleaseMode
);
connectionManager.connection.addObserver( callback );
return connectionManager;
} }
} }

View File

@ -29,6 +29,8 @@ import java.sql.SQLException;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.Interceptor; import org.hibernate.Interceptor;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.jdbc.internal.LogicalConnectionImpl;
/** /**
* An implementation of the <tt>Batcher</tt> interface that does no batching * An implementation of the <tt>Batcher</tt> interface that does no batching

View File

@ -35,6 +35,7 @@ import org.hibernate.ConnectionReleaseMode;
import org.hibernate.engine.jdbc.internal.LogicalConnectionImpl; import org.hibernate.engine.jdbc.internal.LogicalConnectionImpl;
import org.hibernate.engine.jdbc.spi.ConnectionObserver; import org.hibernate.engine.jdbc.spi.ConnectionObserver;
import org.hibernate.engine.jdbc.internal.proxy.ProxyBuilder; import org.hibernate.engine.jdbc.internal.proxy.ProxyBuilder;
import org.hibernate.stat.ConcurrentStatisticsImpl;
import org.hibernate.test.common.BasicTestingJdbcServiceImpl; import org.hibernate.test.common.BasicTestingJdbcServiceImpl;
import org.hibernate.testing.junit.UnitTestCase; import org.hibernate.testing.junit.UnitTestCase;