diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java b/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java index 68959ed419..5e8056a6b5 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java @@ -32,7 +32,6 @@ import org.hibernate.cache.RegionFactory; import org.hibernate.engine.jdbc.JdbcSupport; import org.hibernate.engine.jdbc.batch.internal.BatchBuilder; import org.hibernate.hql.QueryTranslatorFactory; -import org.hibernate.jdbc.BatcherFactory; import org.hibernate.jdbc.util.SQLStatementLogger; import org.hibernate.transaction.TransactionFactory; import org.hibernate.transaction.TransactionManagerLookup; @@ -78,7 +77,6 @@ public final class Settings { private QueryCacheFactory queryCacheFactory; private TransactionFactory transactionFactory; private TransactionManagerLookup transactionManagerLookup; - private BatcherFactory batcherFactory; private BatchBuilder batchBuilder; private QueryTranslatorFactory queryTranslatorFactory; private boolean wrapResultSetsEnabled; @@ -228,10 +226,6 @@ public final class Settings { return flushBeforeCompletionEnabled; } - public BatcherFactory getBatcherFactory() { - return batcherFactory; - } - public BatchBuilder getBatchBuilder() { return batchBuilder; } @@ -419,10 +413,6 @@ public final class Settings { this.flushBeforeCompletionEnabled = flushBeforeCompletionEnabled; } - void setBatcherFactory(BatcherFactory batcher) { - this.batcherFactory = batcher; - } - void setBatcherBuilder(BatchBuilder batchBuilder) { this.batchBuilder = batchBuilder; } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/SettingsFactory.java b/hibernate-core/src/main/java/org/hibernate/cfg/SettingsFactory.java index 9a6c1ecf60..ca2a34f396 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/SettingsFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/SettingsFactory.java @@ -44,9 +44,6 @@ import org.hibernate.engine.jdbc.spi.ExtractedDatabaseMetaData; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.hql.QueryTranslatorFactory; import org.hibernate.internal.util.config.ConfigurationHelper; -import org.hibernate.jdbc.BatcherFactory; -import org.hibernate.jdbc.BatchingBatcherFactory; -import org.hibernate.jdbc.NonBatchingBatcherFactory; import org.hibernate.jdbc.util.SQLStatementLogger; import org.hibernate.transaction.TransactionFactory; import org.hibernate.transaction.TransactionFactoryFactory; @@ -115,7 +112,6 @@ public class SettingsFactory implements Serializable { boolean jdbcBatchVersionedData = ConfigurationHelper.getBoolean(Environment.BATCH_VERSIONED_DATA, properties, false); if (batchSize>0) log.info("JDBC batch updates for versioned data: " + enabledDisabled(jdbcBatchVersionedData) ); settings.setJdbcBatchVersionedData(jdbcBatchVersionedData); - settings.setBatcherFactory( createBatcherFactory(properties, batchSize) ); settings.setBatcherBuilder( createBatchBuilder(properties, batchSize) ); boolean useScrollableResultSets = ConfigurationHelper.getBoolean(Environment.USE_SCROLLABLE_RESULTSET, properties, meta.supportsScrollableResults()); @@ -350,47 +346,25 @@ public class SettingsFactory implements Serializable { } } - protected BatcherFactory createBatcherFactory(Properties properties, int batchSize) { - String batcherClass = properties.getProperty(Environment.BATCH_STRATEGY); - BatcherFactory batcherFactory = null; - if (batcherClass==null) { - batcherFactory = batchSize == 0 - ? new NonBatchingBatcherFactory() - : new BatchingBatcherFactory( ); - } - else { - log.info("Batcher factory: " + batcherClass); - try { - batcherFactory = (BatcherFactory) ReflectHelper.classForName(batcherClass).newInstance(); - } - catch (Exception cnfe) { - throw new HibernateException("could not instantiate BatcherFactory: " + batcherClass, cnfe); - } - } - batcherFactory.setJdbcBatchSize( batchSize ); - return batcherFactory; - } - protected BatchBuilder createBatchBuilder(Properties properties, int batchSize) { - //FIXME: uncomment to use BatchBuilder - /* String batchBuilderClass = properties.getProperty(Environment.BATCH_STRATEGY); + BatchBuilder batchBuilder; if (batchBuilderClass==null) { - return batchSize > 0 + batchBuilder = batchSize > 0 ? new BatchBuilder( batchSize ) : new BatchBuilder(); } else { - log.info("Batcher factory: " + batchBuilderClass); + log.info("Batch factory: " + batchBuilderClass); try { - return (BatchBuilder) ReflectHelper.classForName(batchBuilderClass).newInstance(); + batchBuilder = (BatchBuilder) ReflectHelper.classForName(batchBuilderClass).newInstance(); } catch (Exception cnfe) { throw new HibernateException("could not instantiate BatchBuilder: " + batchBuilderClass, cnfe); } } - */ - return null; + batchBuilder.setJdbcBatchSize( batchSize ); + return batchBuilder; } protected TransactionFactory createTransactionFactory(Properties properties) { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/AbstractBatchImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/AbstractBatchImpl.java index 1f58e3bda9..5d171f4a38 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/AbstractBatchImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/AbstractBatchImpl.java @@ -23,7 +23,6 @@ */ package org.hibernate.engine.jdbc.batch.internal; -import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.LinkedHashMap; @@ -34,9 +33,8 @@ import org.slf4j.LoggerFactory; import org.hibernate.engine.jdbc.batch.spi.Batch; import org.hibernate.engine.jdbc.batch.spi.BatchObserver; -import org.hibernate.engine.jdbc.spi.JdbcServices; -import org.hibernate.engine.jdbc.spi.LogicalConnectionImplementor; -import org.hibernate.engine.jdbc.internal.proxy.ProxyBuilder; +import org.hibernate.engine.jdbc.spi.SQLExceptionHelper; +import org.hibernate.engine.jdbc.spi.SQLStatementLogger; /** * Convenience base class for implementors of the Batch interface. @@ -46,16 +44,21 @@ import org.hibernate.engine.jdbc.internal.proxy.ProxyBuilder; public abstract class AbstractBatchImpl implements Batch { private static final Logger log = LoggerFactory.getLogger( AbstractBatchImpl.class ); + private final SQLStatementLogger statementLogger; + private final SQLExceptionHelper exceptionHelper; private Object key; - private LogicalConnectionImplementor logicalConnection; - private Connection connectionProxy; private LinkedHashMap statements = new LinkedHashMap(); private LinkedHashSet observers = new LinkedHashSet(); - protected AbstractBatchImpl(Object key, LogicalConnectionImplementor logicalConnection) { + protected AbstractBatchImpl(Object key, + SQLStatementLogger statementLogger, + SQLExceptionHelper exceptionHelper) { + if ( key == null || statementLogger == null || exceptionHelper == null ) { + throw new IllegalArgumentException( "key, statementLogger, and exceptionHelper must be non-null." ); + } this.key = key; - this.logicalConnection = logicalConnection; - this.connectionProxy = ProxyBuilder.buildConnection( logicalConnection ); + this.statementLogger = statementLogger; + this.exceptionHelper = exceptionHelper; } /** @@ -67,12 +70,21 @@ public abstract class AbstractBatchImpl implements Batch { protected abstract void doExecuteBatch(); /** - * Convenience access to the underlying JDBC services. + * Convenience access to the SQLException helper. + * + * @return The underlying SQLException helper. + */ + protected SQLExceptionHelper getSqlExceptionHelper() { + return exceptionHelper; + } + + /** + * Convenience access to the SQL statement logger. * * @return The underlying JDBC services. */ - protected JdbcServices getJdbcServices() { - return logicalConnection.getJdbcServices(); + protected SQLStatementLogger getSqlStatementLogger() { + return statementLogger; } /** @@ -101,31 +113,39 @@ public abstract class AbstractBatchImpl implements Batch { /** * {@inheritDoc} */ - public final PreparedStatement getBatchStatement(String sql, boolean callable) { + public final PreparedStatement getBatchStatement(Object key, String sql) { + checkConsistentBatchKey( key ); + if ( sql == null ) { + throw new IllegalArgumentException( "sql must be non-null." ); + } PreparedStatement statement = statements.get( sql ); - if ( statement == null ) { - statement = buildBatchStatement( sql, callable ); - statements.put( sql, statement ); - } - else { - log.debug( "reusing batch statement" ); - getJdbcServices().getSqlStatementLogger().logStatement( sql ); - } + if ( statement != null ) { + log.debug( "reusing prepared statement" ); + statementLogger.logStatement( sql ); + } return statement; } - private PreparedStatement buildBatchStatement(String sql, boolean callable) { - try { - if ( callable ) { - return connectionProxy.prepareCall( sql ); - } - else { - return connectionProxy.prepareStatement( sql ); - } + /** + * {@inheritDoc} + */ + // TODO: should this be final??? + @Override + public void addBatchStatement(Object key, String sql, PreparedStatement preparedStatement) { + checkConsistentBatchKey( key ); + if ( sql == null ) { + throw new IllegalArgumentException( "sql must be non-null." ); } - catch ( SQLException sqle ) { - log.error( "sqlexception escaped proxy", sqle ); - throw getJdbcServices().getSqlExceptionHelper().convert( sqle, "could not prepare batch statement", sql ); + if ( statements.put( sql, preparedStatement ) != null ) { + log.error( "PreparedStatement was already in the batch, [" + sql + "]." ); + } + } + + protected void checkConsistentBatchKey(Object key) { + if ( ! this.key.equals( key ) ) { + throw new IllegalStateException( + "specified key ["+ key + "] is different from internal batch key [" + this.key + "]." + ); } } @@ -142,7 +162,7 @@ public abstract class AbstractBatchImpl implements Batch { doExecuteBatch(); } finally { - releaseStatements(); + release(); } } finally { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/BatchBuilder.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/BatchBuilder.java index 85febd5946..e01a7047b3 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/BatchBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/BatchBuilder.java @@ -28,6 +28,8 @@ import org.slf4j.LoggerFactory; import org.hibernate.engine.jdbc.batch.spi.Batch; import org.hibernate.engine.jdbc.spi.LogicalConnectionImplementor; +import org.hibernate.engine.jdbc.spi.SQLExceptionHelper; +import org.hibernate.engine.jdbc.spi.SQLStatementLogger; /** * A builder for {@link Batch} instances. @@ -46,15 +48,17 @@ public class BatchBuilder { this.size = size; } - public void setSize(int size) { + public void setJdbcBatchSize(int size) { this.size = size; } - public Batch buildBatch(Object key, LogicalConnectionImplementor logicalConnection) { + public Batch buildBatch(Object key, + SQLStatementLogger statementLogger, + SQLExceptionHelper exceptionHelper) { log.trace( "building batch [size={}]", size ); return size > 1 - ? new BatchingBatch( key, logicalConnection, size ) - : new NonBatchingBatch( key, logicalConnection ); + ? new BatchingBatch( key, statementLogger, exceptionHelper, size ) + : new NonBatchingBatch( key, statementLogger, exceptionHelper ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/BatchingBatch.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/BatchingBatch.java index d5d036e42a..9dc1b27020 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/BatchingBatch.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/BatchingBatch.java @@ -25,18 +25,24 @@ package org.hibernate.engine.jdbc.batch.internal; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; -import org.hibernate.engine.jdbc.spi.LogicalConnectionImplementor; +import org.hibernate.engine.jdbc.spi.SQLExceptionHelper; +import org.hibernate.engine.jdbc.spi.SQLStatementLogger; import org.hibernate.jdbc.Expectation; /** - * A {@link org.hibernate.engine.jdbc.batch.spi.Batch} implementation which does batching based on a given size. Once the batch size is exceeded, the - * batch is implicitly executed. + * A {@link org.hibernate.engine.jdbc.batch.spi.Batch} implementation which does + * batching based on a given size. Once the batch size is reached for a statement + * in the batch, the entire batch is implicitly executed. * * @author Steve Ebersole */ @@ -44,33 +50,54 @@ public class BatchingBatch extends AbstractBatchImpl { private static final Logger log = LoggerFactory.getLogger( BatchingBatch.class ); private final int batchSize; - private Expectation[] expectations; - private int batchPosition; - public BatchingBatch(Object key, LogicalConnectionImplementor logicalConnection, int batchSize) { - super( key, logicalConnection ); + // TODO: A Map is used for expectations so it is possible to track when a batch + // is full (i.e., when the batch for a particular statement exceeds batchSize) + // Until HHH-5797 is fixed, there will only be 1 statement in a batch, so it won't + // be necessary to track expectations by statement. + private Map> expectationsBySql; + private int maxBatchPosition; + + public BatchingBatch(Object key, + SQLStatementLogger statementLogger, + SQLExceptionHelper exceptionHelper, + int batchSize) { + super( key, statementLogger, exceptionHelper ); this.batchSize = batchSize; - this.expectations = new Expectation[ batchSize ]; + this.expectationsBySql = new HashMap>(); } /** * {@inheritDoc} */ - public void addToBatch(Expectation expectation) { - if ( !expectation.canBeBatched() ) { + public void addToBatch(Object key, String sql, Expectation expectation) { + checkConsistentBatchKey( key ); + if ( sql == null || expectation == null ) { + throw new AssertionFailure( "sql or expection was null." ); + } + if ( ! expectation.canBeBatched() ) { throw new HibernateException( "attempting to batch an operation which cannot be batched" ); } - for ( Map.Entry entry : getStatements().entrySet() ) { - try { - entry.getValue().addBatch(); - } - catch ( SQLException e ) { - log.error( "sqlexception escaped proxy", e ); - throw getJdbcServices().getSqlExceptionHelper().convert( e, "could not perform addBatch", entry.getKey() ); - } + final PreparedStatement statement = getStatements().get( sql ); + try { + statement.addBatch(); } - expectations[ batchPosition++ ] = expectation; - if ( batchPosition == batchSize ) { + catch ( SQLException e ) { + log.error( "sqlexception escaped proxy", e ); + throw getSqlExceptionHelper().convert( e, "could not perform addBatch", sql ); + } + List expectations = expectationsBySql.get( sql ); + if ( expectations == null ) { + expectations = new ArrayList( batchSize ); + expectationsBySql.put( sql, expectations ); + } + expectations.add( expectation ); + maxBatchPosition = Math.max( maxBatchPosition, expectations.size() ); + + // TODO: When HHH-5797 is fixed the following if-block should probably be moved before + // adding the batch to the current statement (to detect that we have finished + // with the previous entity). + if ( maxBatchPosition == batchSize ) { notifyObserversImplicitExecution(); doExecuteBatch(); } @@ -80,46 +107,89 @@ public class BatchingBatch extends AbstractBatchImpl { * {@inheritDoc} */ protected void doExecuteBatch() { - if ( batchPosition == 0 ) { + if ( maxBatchPosition == 0 ) { log.debug( "no batched statements to execute" ); } else { if ( log.isDebugEnabled() ) { - log.debug( "Executing batch size: " + batchPosition ); + log.debug( "Executing {} statements with maximum batch size {} ", + getStatements().size(), maxBatchPosition + ); } - try { - for ( Map.Entry entry : getStatements().entrySet() ) { - try { - final PreparedStatement statement = entry.getValue(); - checkRowCounts( statement.executeBatch(), statement ); - } - catch ( SQLException e ) { - log.error( "sqlexception escaped proxy", e ); - throw getJdbcServices().getSqlExceptionHelper() - .convert( e, "could not perform addBatch", entry.getKey() ); - } - } + executeStatements(); } catch ( RuntimeException re ) { log.error( "Exception executing batch [{}]", re.getMessage() ); throw re; } finally { - batchPosition = 0; + for ( List expectations : expectationsBySql.values() ) { + expectations.clear(); + } + maxBatchPosition = 0; } - } } - private void checkRowCounts(int[] rowCounts, PreparedStatement ps) throws SQLException, HibernateException { + private void executeStatements() { + for ( Map.Entry entry : getStatements().entrySet() ) { + final String sql = entry.getKey(); + final PreparedStatement statement = entry.getValue(); + final List expectations = expectationsBySql.get( sql ); + if ( batchSize < expectations.size() ) { + throw new IllegalStateException( + "Number of expectations [" + expectations.size() + + "] is greater than batch size [" + batchSize + + "] for statement [" + sql + + "]" + ); + } + if ( expectations.size() > 0 ) { + if ( log.isDebugEnabled() ) { + log.debug( "Executing with batch of size {}: {}", expectations.size(), sql ); + } + executeStatement( sql, statement, expectations ); + expectations.clear(); + } + else { + if ( log.isDebugEnabled() ) { + log.debug( "Skipped executing because batch size is 0: ", sql ); + } + } + } + } + + private void executeStatement(String sql, PreparedStatement ps, List expectations) { + try { + checkRowCounts( sql, ps.executeBatch(), ps, expectations ); + } + catch ( SQLException e ) { + log.error( "sqlexception escaped proxy", e ); + throw getSqlExceptionHelper() + .convert( e, "could not execute statement: " + sql ); + } + } + + private void checkRowCounts(String sql, int[] rowCounts, PreparedStatement ps, List expectations) { int numberOfRowCounts = rowCounts.length; - if ( numberOfRowCounts != batchPosition ) { + if ( numberOfRowCounts != expectations.size() ) { log.warn( "JDBC driver did not return the expected number of row counts" ); } - for ( int i = 0; i < numberOfRowCounts; i++ ) { - expectations[i].verifyOutcome( rowCounts[i], ps, i ); + try { + for ( int i = 0; i < numberOfRowCounts; i++ ) { + expectations.get( i ).verifyOutcome( rowCounts[i], ps, i ); + } + } + catch ( SQLException e ) { + log.error( "sqlexception escaped proxy", e ); + throw getSqlExceptionHelper() + .convert( e, "row count verification failed for statement: ", sql ); } } + public void release() { + expectationsBySql.clear(); + maxBatchPosition = 0; + } } \ No newline at end of file diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/NonBatchingBatch.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/NonBatchingBatch.java index e9becd14b6..64eb6b116a 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/NonBatchingBatch.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/NonBatchingBatch.java @@ -25,12 +25,12 @@ package org.hibernate.engine.jdbc.batch.internal; import java.sql.PreparedStatement; import java.sql.SQLException; -import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.hibernate.engine.jdbc.spi.LogicalConnectionImplementor; +import org.hibernate.engine.jdbc.spi.SQLExceptionHelper; +import org.hibernate.engine.jdbc.spi.SQLStatementLogger; import org.hibernate.jdbc.Expectation; /** @@ -42,22 +42,26 @@ import org.hibernate.jdbc.Expectation; public class NonBatchingBatch extends AbstractBatchImpl { private static final Logger log = LoggerFactory.getLogger( NonBatchingBatch.class ); - protected NonBatchingBatch(Object key, LogicalConnectionImplementor logicalConnection) { - super( key, logicalConnection ); + protected NonBatchingBatch(Object key, + SQLStatementLogger statementLogger, + SQLExceptionHelper exceptionHelper) { + super( key, statementLogger, exceptionHelper ); } - public void addToBatch(Expectation expectation) { + public void addToBatch(Object key, String sql, Expectation expectation) { + checkConsistentBatchKey( key ); + if ( sql == null ) { + throw new IllegalArgumentException( "sql must be non-null." ); + } notifyObserversImplicitExecution(); - for ( Map.Entry entry : getStatements().entrySet() ) { - try { - final PreparedStatement statement = entry.getValue(); - final int rowCount = statement.executeUpdate(); - expectation.verifyOutcome( rowCount, statement, 0 ); - } - catch ( SQLException e ) { - log.error( "sqlexception escaped proxy", e ); - throw getJdbcServices().getSqlExceptionHelper().convert( e, "could not execute batch statement", entry.getKey() ); - } + try { + final PreparedStatement statement = getStatements().get( sql ); + final int rowCount = statement.executeUpdate(); + expectation.verifyOutcome( rowCount, statement, 0 ); + } + catch ( SQLException e ) { + log.error( "sqlexception escaped proxy", e ); + throw getSqlExceptionHelper().convert( e, "could not execute batch statement", sql ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/spi/Batch.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/spi/Batch.java index aeda16f69b..7c99332100 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/spi/Batch.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/spi/Batch.java @@ -52,20 +52,30 @@ public interface Batch { public void addObserver(BatchObserver observer); /** - * Get a statement which is part of the batch, creating if necessary (and storing for next time). + * Get a statement which is part of the batch. * * @param sql The SQL statement. - * @param callable Is the SQL statement callable? - * @return The prepared statement instance, representing the SQL statement. + * @return the prepared statement representing the SQL statement, if the batch contained it; + * null, otherwise. */ - public PreparedStatement getBatchStatement(String sql, boolean callable); + public PreparedStatement getBatchStatement(Object key, String sql); + + /** + * Add a prepared statement to the batch. + * + * @param sql The SQL statement. + */ + public void addBatchStatement(Object key, String sql, PreparedStatement preparedStatement); + /** * Indicates completion of the current part of the batch. * + * @param key + * @param sql * @param expectation The expectation for the part's result. */ - public void addToBatch(Expectation expectation); + public void addToBatch(Object key, String sql, Expectation expectation); /** * Execute this batch. diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/ConnectionManagerImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/ConnectionManagerImpl.java index 306c65b86a..dddaa20eca 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/ConnectionManagerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/ConnectionManagerImpl.java @@ -30,7 +30,6 @@ import java.io.ObjectOutputStream; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; import org.slf4j.Logger; @@ -41,12 +40,11 @@ import org.hibernate.ConnectionReleaseMode; import org.hibernate.HibernateException; import org.hibernate.Interceptor; import org.hibernate.ScrollMode; -import org.hibernate.TransactionException; import org.hibernate.engine.SessionFactoryImplementor; -import org.hibernate.engine.jdbc.internal.proxy.ProxyBuilder; +import org.hibernate.engine.jdbc.batch.internal.BatchBuilder; +import org.hibernate.engine.jdbc.batch.spi.Batch; import org.hibernate.engine.jdbc.spi.ConnectionManager; import org.hibernate.engine.jdbc.spi.ConnectionObserver; -import org.hibernate.jdbc.Batcher; import org.hibernate.jdbc.Expectation; /** @@ -67,15 +65,13 @@ public class ConnectionManagerImpl implements ConnectionManager { // TODO: check if it's ok to change the method names in Callback - private transient SessionFactoryImplementor factory; - private transient Connection proxiedConnection; private transient Interceptor interceptor; private final Callback callback; - private long transactionTimeout = -1; - boolean isTransactionTimeoutSet; - private transient LogicalConnectionImpl logicalConnection; + private transient StatementPreparer statementPreparer; + private final transient BatchBuilder batchBuilder; + private Batch batch; /** * Constructs a ConnectionManager. @@ -99,8 +95,7 @@ public class ConnectionManagerImpl implements ConnectionManager { suppliedConnection, releaseMode, factory.getJdbcServices(), - factory.getStatistics() != null ? factory.getStatisticsImplementor() : null, - factory.getSettings().getBatcherFactory() + factory.getStatistics() != null ? factory.getStatisticsImplementor() : null ) ); } @@ -114,16 +109,12 @@ public class ConnectionManagerImpl implements ConnectionManager { Interceptor interceptor, LogicalConnectionImpl logicalConnection ) { - this.factory = factory; this.callback = callback; this.interceptor = interceptor; - setupConnection( logicalConnection ); - } - - private void setupConnection(LogicalConnectionImpl logicalConnection) { this.logicalConnection = logicalConnection; this.logicalConnection.addObserver( callback ); - proxiedConnection = ProxyBuilder.buildConnection( logicalConnection ); + this.statementPreparer = new StatementPreparer( logicalConnection, factory.getSettings() ); + this.batchBuilder = factory.getSettings().getBatchBuilder(); } /** @@ -271,7 +262,9 @@ public class ConnectionManagerImpl implements ConnectionManager { log.debug( "transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!" ); } } - unsetTransactionTimeout(); + if ( statementPreparer != null ) { + statementPreparer.unsetTransactionTimeout(); + } } private boolean isAfterTransactionRelease() { @@ -282,37 +275,15 @@ public class ConnectionManagerImpl implements ConnectionManager { return logicalConnection.getConnectionReleaseMode() == ConnectionReleaseMode.ON_CLOSE; } - public boolean isLogicallyConnected() { + private boolean isLogicallyConnected() { return logicalConnection != null && logicalConnection.isOpen(); } @Override public void setTransactionTimeout(int seconds) { - isTransactionTimeoutSet = true; - transactionTimeout = System.currentTimeMillis() / 1000 + seconds; + statementPreparer.setTransactionTimeout( seconds ); } - /** - * Unset the transaction timeout, called after the end of a - * transaction. - */ - private void unsetTransactionTimeout() { - isTransactionTimeoutSet = false; - } - - private void setStatementTimeout(PreparedStatement preparedStatement) throws SQLException { - if ( isTransactionTimeoutSet ) { - int timeout = (int) ( transactionTimeout - ( System.currentTimeMillis() / 1000 ) ); - if ( timeout <= 0) { - throw new TransactionException("transaction timeout expired"); - } - else { - preparedStatement.setQueryTimeout(timeout); - } - } - } - - /** * To be called after Session completion. Used to release the JDBC * connection. @@ -337,6 +308,7 @@ public class ConnectionManagerImpl implements ConnectionManager { if ( ! isLogicallyConnected() ) { throw new IllegalStateException( "cannot manually disconnect because not logically connected." ); } + releaseBatch(); return logicalConnection.manualDisconnect(); } @@ -382,10 +354,14 @@ public class ConnectionManagerImpl implements ConnectionManager { } try { log.trace( "performing cleanup" ); + releaseBatch(); + statementPreparer.close(); Connection c = logicalConnection.close(); return c; } finally { + batch = null; + statementPreparer = null; logicalConnection = null; } } @@ -412,105 +388,68 @@ public class ConnectionManagerImpl implements ConnectionManager { afterStatement(); } - private abstract class StatementPreparer { - private final String sql; - StatementPreparer(String sql) { - this.sql = getSQL( sql ); - } - public String getSqlToPrepare() { - return sql; - } - abstract PreparedStatement doPrepare() throws SQLException; - public void afterPrepare(PreparedStatement preparedStatement) throws SQLException { - setStatementTimeout( preparedStatement ); - } - } - /** * Get a non-batchable prepared statement to use for inserting / deleting / updating, * using JDBC3 getGeneratedKeys ({@link java.sql.Connection#prepareStatement(String, int)}). + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, it will be + * released when the session is closed or disconnected. */ + @Override public PreparedStatement prepareStatement(String sql, final int autoGeneratedKeys) throws HibernateException { - if ( autoGeneratedKeys == PreparedStatement.RETURN_GENERATED_KEYS ) { - checkAutoGeneratedKeysSupportEnabled(); - } - StatementPreparer statementPreparer = new StatementPreparer( sql ) { - public PreparedStatement doPrepare() throws SQLException { - return proxiedConnection.prepareStatement( getSqlToPrepare(), autoGeneratedKeys ); - } - }; - return prepareStatement( statementPreparer, true ); + executeBatch(); + return statementPreparer.prepareStatement( getSQL( sql ), autoGeneratedKeys ); } /** * Get a non-batchable prepared statement to use for inserting / deleting / updating. * using JDBC3 getGeneratedKeys ({@link java.sql.Connection#prepareStatement(String, String[])}). + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, it will be + * released when the session is closed or disconnected. */ + @Override public PreparedStatement prepareStatement(String sql, final String[] columnNames) { - checkAutoGeneratedKeysSupportEnabled(); - StatementPreparer statementPreparer = new StatementPreparer( sql ) { - public PreparedStatement doPrepare() throws SQLException { - return proxiedConnection.prepareStatement( getSqlToPrepare(), columnNames ); - } - }; - return prepareStatement( statementPreparer, true ); - } - - private void checkAutoGeneratedKeysSupportEnabled() { - if ( ! factory.getSettings().isGetGeneratedKeysEnabled() ) { - throw new AssertionFailure("getGeneratedKeys() support is not enabled"); - } + executeBatch(); + return statementPreparer.prepareStatement( getSQL( sql ), columnNames ); } /** * Get a non-batchable prepared statement to use for selecting. Does not * result in execution of the current batch. + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, + * it will be released when the session is closed or disconnected. */ + @Override public PreparedStatement prepareSelectStatement(String sql) { - return prepareStatement( sql, false, false ); + return statementPreparer.prepareStatement( getSQL( sql ), false ); } /** * Get a non-batchable prepared statement to use for inserting / deleting / updating. + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, it will be + * released when the session is closed or disconnected. */ + @Override public PreparedStatement prepareStatement(String sql, final boolean isCallable) { - return prepareStatement( sql, isCallable, true ); + executeBatch(); + return statementPreparer.prepareStatement( getSQL( sql ), isCallable ); } /** * Get a non-batchable callable statement to use for inserting / deleting / updating. + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, it will be + * released when the session is closed or disconnected. */ + @Override public CallableStatement prepareCallableStatement(String sql) { + executeBatch(); log.trace("preparing callable statement"); - return CallableStatement.class.cast( prepareStatement( sql, true, true ) ); - } - - public PreparedStatement prepareStatement(String sql, final boolean isCallable, boolean forceExecuteBatch) { - StatementPreparer statementPreparer = new StatementPreparer( sql ) { - public PreparedStatement doPrepare() throws SQLException { - return prepareStatementInternal( getSqlToPrepare(), isCallable ); - } - }; - return prepareStatement( statementPreparer, forceExecuteBatch ); - } - - private PreparedStatement prepareStatementInternal(String sql, boolean isCallable) throws SQLException { - return isCallable ? - proxiedConnection.prepareCall( sql ) : - proxiedConnection.prepareStatement( sql ); - } - - private PreparedStatement prepareScrollableStatementInternal(String sql, - ScrollMode scrollMode, - boolean isCallable) throws SQLException { - return isCallable ? - proxiedConnection.prepareCall( - sql, scrollMode.toResultSetType(), ResultSet.CONCUR_READ_ONLY - ) : - proxiedConnection.prepareStatement( - sql, scrollMode.toResultSetType(), ResultSet.CONCUR_READ_ONLY - ); + return CallableStatement.class.cast( statementPreparer.prepareStatement( getSQL( sql ), true ) ); } /** @@ -518,105 +457,97 @@ public class ConnectionManagerImpl implements ConnectionManager { * (might be called many times before a single call to executeBatch()). * After setting parameters, call addToBatch - do not execute the * statement explicitly. - * @see org.hibernate.jdbc.Batcher#addToBatch + * @see org.hibernate.engine.jdbc.batch.spi.Batch#addToBatch + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, it will be + * released when the session is closed or disconnected. */ - public PreparedStatement prepareBatchStatement(String sql, boolean isCallable) { - String batchUpdateSQL = getSQL( sql ); - - PreparedStatement batchUpdate = getBatcher().getStatement( batchUpdateSQL ); - if ( batchUpdate == null ) { - batchUpdate = prepareStatement( batchUpdateSQL, isCallable, true ); // calls executeBatch() - getBatcher().setStatement( batchUpdateSQL, batchUpdate ); + @Override + public PreparedStatement prepareBatchStatement(Object key, String sql, boolean isCallable) { + if ( key == null ) { + throw new IllegalArgumentException( "batch key must be non-null." ); } - else { - log.debug( "reusing prepared statement" ); - factory.getJdbcServices().getSqlStatementLogger().logStatement( batchUpdateSQL ); + String actualSQL = getSQL( sql ); + PreparedStatement batchUpdate = null; + if ( batch != null ) { + if ( key.equals( batch.getKey() ) ) { + batchUpdate = batch.getBatchStatement( key, actualSQL ); + } + else { + batch.execute(); + batch = null; + } + } + if ( batch == null ) { + batch = batchBuilder.buildBatch( + key, + logicalConnection.getJdbcServices().getSqlStatementLogger(), + logicalConnection.getJdbcServices().getSqlExceptionHelper() + ); + } + if ( batchUpdate == null ) { + batchUpdate = statementPreparer.prepareStatement( actualSQL, isCallable ); + batch.addBatchStatement( key, actualSQL, batchUpdate ); } return batchUpdate; } - private Batcher getBatcher() { - return logicalConnection.getBatcher(); - } - /** - * Get a prepared statement for use in loading / querying. If not explicitly - * released by closeQueryStatement(), it will be released when the - * session is closed or disconnected. + * Get a prepared statement for use in loading / querying. Does not + * result in execution of the current batch. + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, + * it will be released when the session is closed or disconnected. */ + @Override public PreparedStatement prepareQueryStatement( String sql, final boolean isScrollable, final ScrollMode scrollMode, - final boolean isCallable - ) { - if ( isScrollable && ! factory.getSettings().isScrollableResultSetsEnabled() ) { - throw new AssertionFailure("scrollable result sets are not enabled"); - } - StatementPreparer statementPreparer = new StatementPreparer( sql ) { - public PreparedStatement doPrepare() throws SQLException { - PreparedStatement ps = - isScrollable ? - prepareScrollableStatementInternal( getSqlToPrepare(), scrollMode, isCallable ) : - prepareStatementInternal( getSqlToPrepare(), isCallable ) - ; - return ps; - } - public void afterPrepare(PreparedStatement preparedStatement) throws SQLException { - super.afterPrepare( preparedStatement ); - setStatementFetchSize( preparedStatement, getSqlToPrepare() ); - logicalConnection.getResourceRegistry().registerLastQuery( preparedStatement ); - } - }; - return prepareStatement( statementPreparer, false ); - } - - private void setStatementFetchSize(PreparedStatement statement, String sql) throws SQLException { - if ( factory.getSettings().getJdbcFetchSize() != null ) { - statement.setFetchSize( factory.getSettings().getJdbcFetchSize() ); - } - } - - private PreparedStatement prepareStatement(StatementPreparer preparer, boolean forceExecuteBatch) { - if ( forceExecuteBatch ) { - executeBatch(); - } - try { - PreparedStatement ps = preparer.doPrepare(); - preparer.afterPrepare( ps ); - return ps; - } - catch ( SQLException sqle ) { - log.error( "sqlexception escaped proxy", sqle ); - throw logicalConnection.getJdbcServices().getSqlExceptionHelper().convert( - sqle, "could not prepare statement", preparer.getSqlToPrepare() - ); - } + final boolean isCallable) { + PreparedStatement ps = ( + isScrollable ? + statementPreparer.prepareScrollableQueryStatement( + getSQL( sql ), scrollMode, isCallable + ) : + statementPreparer.prepareQueryStatement( + getSQL( sql ), isCallable + ) + ); + logicalConnection.getResourceRegistry().registerLastQuery( ps ); + return ps; } /** * Cancel the current query statement */ + @Override public void cancelLastQuery() throws HibernateException { logicalConnection.getResourceRegistry().cancelLastQuery(); } - public void abortBatch(SQLException sqle) { - getBatcher().abortBatch( sqle ); - } - - public void addToBatch(Expectation expectation ) { - try { - getBatcher().addToBatch( expectation ); - } - catch (SQLException sqle) { - throw logicalConnection.getJdbcServices().getSqlExceptionHelper().convert( - sqle, "could not add to batch statement" ); - } + @Override + public void addToBatch(Object batchKey, String sql, Expectation expectation) { + batch.addToBatch( batchKey, sql, expectation ); } + @Override public void executeBatch() throws HibernateException { - getBatcher().executeBatch(); + if ( batch != null ) { + batch.execute(); + batch.release(); // needed? + } + } + + @Override + public void abortBatch() { + releaseBatch(); + } + + private void releaseBatch() { + if ( batch != null ) { + batch.release(); + } } private String getSQL(String sql) { @@ -673,8 +604,7 @@ public class ConnectionManagerImpl implements ConnectionManager { ois, factory.getJdbcServices(), factory.getStatistics() != null ? factory.getStatisticsImplementor() : null, - connectionReleaseMode, - factory.getSettings().getBatcherFactory() + connectionReleaseMode ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcResourceRegistryImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcResourceRegistryImpl.java index a4c0249342..bcace088a0 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcResourceRegistryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcResourceRegistryImpl.java @@ -23,14 +23,11 @@ */ package org.hibernate.engine.jdbc.internal; -import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.Map; import java.util.Set; @@ -42,8 +39,6 @@ import org.hibernate.engine.jdbc.spi.JdbcWrapper; import org.hibernate.engine.jdbc.spi.SQLExceptionHelper; import org.hibernate.engine.jdbc.spi.JdbcResourceRegistry; import org.hibernate.engine.jdbc.spi.InvalidatableWrapper; -import org.hibernate.jdbc.Batcher; -import org.hibernate.jdbc.BatcherFactory; /** * Standard implementation of the {@link org.hibernate.engine.jdbc.spi.JdbcResourceRegistry} contract @@ -56,13 +51,11 @@ public class JdbcResourceRegistryImpl implements JdbcResourceRegistry { private final HashMap> xref = new HashMap>(); private final Set unassociatedResultSets = new HashSet(); private final SQLExceptionHelper exceptionHelper; - private final Batcher batcher; private Statement lastQuery; - public JdbcResourceRegistryImpl(SQLExceptionHelper exceptionHelper, BatcherFactory batcherFactory) { + public JdbcResourceRegistryImpl(SQLExceptionHelper exceptionHelper) { this.exceptionHelper = exceptionHelper; - this.batcher = batcherFactory.createBatcher( exceptionHelper ); } public void register(Statement statement) { @@ -73,10 +66,6 @@ public class JdbcResourceRegistryImpl implements JdbcResourceRegistry { xref.put( statement, null ); } - public Batcher getBatcher() { - return batcher; - } - @SuppressWarnings({ "unchecked" }) public void registerLastQuery(Statement statement) { log.trace( "registering last query statement [{}]", statement ); @@ -183,7 +172,6 @@ public class JdbcResourceRegistryImpl implements JdbcResourceRegistry { } private void cleanup() { - batcher.closeStatements(); for ( Map.Entry> entry : xref.entrySet() ) { if ( entry.getValue() != null ) { for ( ResultSet resultSet : entry.getValue() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/LogicalConnectionImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/LogicalConnectionImpl.java index 35a8f091b8..8191d94162 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/LogicalConnectionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/LogicalConnectionImpl.java @@ -41,8 +41,6 @@ import org.hibernate.engine.jdbc.spi.JdbcResourceRegistry; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.ConnectionObserver; import org.hibernate.engine.jdbc.spi.LogicalConnectionImplementor; -import org.hibernate.jdbc.Batcher; -import org.hibernate.jdbc.BatcherFactory; import org.hibernate.jdbc.BorrowedConnectionProxy; import org.hibernate.stat.StatisticsImplementor; @@ -60,7 +58,7 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor { private final ConnectionReleaseMode connectionReleaseMode; private final JdbcServices jdbcServices; private final StatisticsImplementor statisticsImplementor; - private final JdbcResourceRegistryImpl jdbcResourceRegistry; + private final JdbcResourceRegistry jdbcResourceRegistry; private final List observers = new ArrayList(); private boolean releasesEnabled = true; @@ -72,31 +70,20 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor { public LogicalConnectionImpl(Connection userSuppliedConnection, ConnectionReleaseMode connectionReleaseMode, JdbcServices jdbcServices, - StatisticsImplementor statisticsImplementor, - BatcherFactory batcherFactory + StatisticsImplementor statisticsImplementor ) { - this.jdbcServices = jdbcServices; - this.statisticsImplementor = statisticsImplementor; + this( connectionReleaseMode, + jdbcServices, + statisticsImplementor, + userSuppliedConnection != null, + false + ); this.physicalConnection = userSuppliedConnection; - this.connectionReleaseMode = - determineConnectionReleaseMode( - jdbcServices, userSuppliedConnection != null, connectionReleaseMode - ); - this.jdbcResourceRegistry = - new JdbcResourceRegistryImpl( - getJdbcServices().getSqlExceptionHelper(), - batcherFactory - ); - - this.isUserSuppliedConnection = ( userSuppliedConnection != null ); - this.isClosed = false; } - // used for deserialization private LogicalConnectionImpl(ConnectionReleaseMode connectionReleaseMode, JdbcServices jdbcServices, StatisticsImplementor statisticsImplementor, - BatcherFactory batcherFactory, boolean isUserSuppliedConnection, boolean isClosed) { this.connectionReleaseMode = determineConnectionReleaseMode( @@ -105,10 +92,7 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor { this.jdbcServices = jdbcServices; this.statisticsImplementor = statisticsImplementor; this.jdbcResourceRegistry = - new JdbcResourceRegistryImpl( - getJdbcServices().getSqlExceptionHelper(), - batcherFactory - ); + new JdbcResourceRegistryImpl( getJdbcServices().getSqlExceptionHelper() ); this.isUserSuppliedConnection = isUserSuppliedConnection; this.isClosed = isClosed; @@ -230,10 +214,6 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor { return connectionReleaseMode; } - public Batcher getBatcher() { - return jdbcResourceRegistry.getBatcher(); - } - public boolean hasBorrowedConnection() { return borrowedConnection != null; } @@ -436,14 +416,12 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor { public static LogicalConnectionImpl deserialize(ObjectInputStream ois, JdbcServices jdbcServices, StatisticsImplementor statisticsImplementor, - ConnectionReleaseMode connectionReleaseMode, - BatcherFactory batcherFactory + ConnectionReleaseMode connectionReleaseMode ) throws IOException { return new LogicalConnectionImpl( connectionReleaseMode, jdbcServices, statisticsImplementor, - batcherFactory, ois.readBoolean(), ois.readBoolean() ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/StatementPreparer.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/StatementPreparer.java new file mode 100644 index 0000000000..2500f328f9 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/StatementPreparer.java @@ -0,0 +1,276 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.engine.jdbc.internal; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.hibernate.AssertionFailure; +import org.hibernate.HibernateException; +import org.hibernate.ScrollMode; +import org.hibernate.TransactionException; +import org.hibernate.cfg.Settings; +import org.hibernate.engine.jdbc.internal.proxy.ProxyBuilder; +import org.hibernate.engine.jdbc.spi.LogicalConnectionImplementor; +import org.hibernate.engine.jdbc.spi.SQLExceptionHelper; + +/** + * Prepares statements. + * + * @author Gail Badner + */ +public class StatementPreparer { + + private static final Logger log = LoggerFactory.getLogger( StatementPreparer.class ); + + // TODO: Move JDBC settings into a different object... + private final Settings settings; + private final Connection proxiedConnection; + private final SQLExceptionHelper sqlExceptionHelper; + + private long transactionTimeout = -1; + boolean isTransactionTimeoutSet; + + /** + * Constructs a StatementPreparer object + * @param logicalConnection - the logical connection + * @param settings - contains settings configured for preparing statements + */ + public StatementPreparer(LogicalConnectionImplementor logicalConnection, Settings settings) { + this.settings = settings; + proxiedConnection = ProxyBuilder.buildConnection( logicalConnection ); + sqlExceptionHelper = logicalConnection.getJdbcServices().getSqlExceptionHelper(); + } + + private abstract class StatementPreparation { + private final String sql; + protected abstract PreparedStatement doPrepare() throws SQLException; + public StatementPreparation(String sql) { + this.sql = sql; + } + public String getSql() { + return sql; + } + public void postProcess(PreparedStatement preparedStatement) throws SQLException { + setStatementTimeout( preparedStatement ); + } + public PreparedStatement prepareAndPostProcess() { + try { + PreparedStatement ps = doPrepare(); + postProcess( ps ); + return ps; + } + catch ( SQLException sqle ) { + log.error( "sqlexception escaped proxy", sqle ); + throw sqlExceptionHelper.convert( + sqle, "could not prepare statement", sql + ); + } + } + } + + private abstract class QueryStatementPreparation extends StatementPreparation { + QueryStatementPreparation(String sql) { + super( sql ); + } + public void postProcess(PreparedStatement preparedStatement) throws SQLException { + super.postProcess( preparedStatement ); + setStatementFetchSize( preparedStatement ); + } + } + + public void close() { + try { + proxiedConnection.close(); + } + catch (SQLException sqle) { + log.error( "sqlexception escaped proxy", sqle ); + throw sqlExceptionHelper.convert( sqle, "could not close connection proxy" ); + } + } + + /** + * Prepare a statement. If configured, the query timeout is set. + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, + * it will be released when the session is closed or disconnected. + * + * @param sql - the SQL for the statement to be prepared + * @param isCallable - true, if a callable statement is to be prepared + * @return the prepared statement + */ + public PreparedStatement prepareStatement(String sql, final boolean isCallable) { + StatementPreparation statementPreparation = new StatementPreparation( sql ) { + public PreparedStatement doPrepare() throws SQLException { + return isCallable ? + proxiedConnection.prepareCall( getSql() ) : + proxiedConnection.prepareStatement( getSql() ); + } + }; + return statementPreparation.prepareAndPostProcess(); + } + + /** + * Get a prepared statement to use for inserting / deleting / updating, + * using JDBC3 getGeneratedKeys ({@link java.sql.Connection#prepareStatement(String, int)}). + * If configured, the query timeout is set. + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, + * it will be released when the session is closed or disconnected. + + * @param sql - the SQL for the statement to be prepared + * @param autoGeneratedKeys - a flag indicating whether auto-generated + * keys should be returned; one of + * PreparedStatement.RETURN_GENERATED_KEYS or + * Statement.NO_GENERATED_KEYS + * @return the prepared statement + */ + public PreparedStatement prepareStatement(String sql, final int autoGeneratedKeys) + throws HibernateException { + if ( autoGeneratedKeys == PreparedStatement.RETURN_GENERATED_KEYS ) { + checkAutoGeneratedKeysSupportEnabled(); + } + StatementPreparation statementPreparation = new StatementPreparation( sql ) { + public PreparedStatement doPrepare() throws SQLException { + return proxiedConnection.prepareStatement( getSql(), autoGeneratedKeys ); + } + }; + return statementPreparation.prepareAndPostProcess(); + } + + /** + * Get a prepared statement to use for inserting / deleting / updating. + * using JDBC3 getGeneratedKeys ({@link java.sql.Connection#prepareStatement(String, String[])}). + * If configured, the query timeout is set. + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, + * it will be released when the session is closed or disconnected. + */ + public PreparedStatement prepareStatement(String sql, final String[] columnNames) { + checkAutoGeneratedKeysSupportEnabled(); + StatementPreparation preparation = new StatementPreparation( sql ) { + public PreparedStatement doPrepare() throws SQLException { + return proxiedConnection.prepareStatement( getSql(), columnNames ); + } + }; + return preparation.prepareAndPostProcess(); + } + + private void checkAutoGeneratedKeysSupportEnabled() { + if ( ! settings.isGetGeneratedKeysEnabled() ) { + throw new AssertionFailure("getGeneratedKeys() support is not enabled"); + } + } + + /** + * Get a prepared statement for use in loading / querying. + * If configured, the query timeout and statement fetch size are set. + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, + * it will be released when the session is closed or disconnected. + */ + public PreparedStatement prepareQueryStatement( + String sql, + final boolean isCallable + ) { + StatementPreparation prep = new QueryStatementPreparation( sql ) { + public PreparedStatement doPrepare() throws SQLException { + return isCallable ? + proxiedConnection.prepareCall( getSql() ) : + proxiedConnection.prepareStatement( getSql() ); + } + }; + return prep.prepareAndPostProcess(); + } + + /** + * Get a scrollable prepared statement for use in loading / querying. + * If configured, the query timeout and statement fetch size are set. + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, + * it will be released when the session is closed or disconnected. + */ + public PreparedStatement prepareScrollableQueryStatement( + String sql, + final ScrollMode scrollMode, + final boolean isCallable + ) { + if ( ! settings.isScrollableResultSetsEnabled() ) { + throw new AssertionFailure("scrollable result sets are not enabled"); + } + StatementPreparation prep = new QueryStatementPreparation( sql ) { + public PreparedStatement doPrepare() throws SQLException { + return isCallable ? + proxiedConnection.prepareCall( + getSql(), scrollMode.toResultSetType(), ResultSet.CONCUR_READ_ONLY + ) : + proxiedConnection.prepareStatement( + getSql(), scrollMode.toResultSetType(), ResultSet.CONCUR_READ_ONLY + ); + } + }; + return prep.prepareAndPostProcess(); + } + + /** + * Sets the transaction timeout. + * @param seconds - number of seconds until the the transaction times out. + */ + public void setTransactionTimeout(int seconds) { + isTransactionTimeoutSet = true; + transactionTimeout = System.currentTimeMillis() / 1000 + seconds; + } + + /** + * Unset the transaction timeout, called after the end of a + * transaction. + */ + public void unsetTransactionTimeout() { + isTransactionTimeoutSet = false; + } + + private void setStatementTimeout(PreparedStatement preparedStatement) throws SQLException { + if ( isTransactionTimeoutSet ) { + int timeout = (int) ( transactionTimeout - ( System.currentTimeMillis() / 1000 ) ); + if ( timeout <= 0) { + throw new TransactionException("transaction timeout expired"); + } + else { + preparedStatement.setQueryTimeout(timeout); + } + } + } + + private void setStatementFetchSize(PreparedStatement statement) throws SQLException { + if ( settings.getJdbcFetchSize() != null ) { + statement.setFetchSize( settings.getJdbcFetchSize() ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/proxy/PreparedStatementProxyHandler.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/proxy/PreparedStatementProxyHandler.java index cc8f76a2d7..e78e3e32bf 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/proxy/PreparedStatementProxyHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/proxy/PreparedStatementProxyHandler.java @@ -26,6 +26,10 @@ package org.hibernate.engine.jdbc.internal.proxy; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.Statement; +import java.util.Arrays; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Invocation handler for {@link java.sql.PreparedStatement} proxies @@ -33,6 +37,8 @@ import java.sql.Statement; * @author Steve Ebersole */ public class PreparedStatementProxyHandler extends AbstractStatementProxyHandler { + private static final Logger log = LoggerFactory.getLogger( ConnectionProxyHandler.class ); + private final String sql; protected PreparedStatementProxyHandler( @@ -63,6 +69,7 @@ public class PreparedStatementProxyHandler extends AbstractStatementProxyHandler } private void journalParameterBind(Method method, Object[] args) { + log.trace( "binding via {}: []", method.getName(), Arrays.asList( args ) ); } private boolean isExecution(Method method) { diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ConnectionManager.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ConnectionManager.java index 733fa01891..9e7d35fb8a 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ConnectionManager.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/ConnectionManager.java @@ -79,6 +79,10 @@ public interface ConnectionManager extends Serializable { */ void afterStatement(); + /** + * Sets the transaction timeout. + * @param seconds - number of seconds until the the transaction times out. + */ void setTransactionTimeout(int seconds); /** @@ -131,28 +135,43 @@ public interface ConnectionManager extends Serializable { /** * Get a non-batchable prepared statement to use for inserting / deleting / updating, * using JDBC3 getGeneratedKeys ({@link java.sql.Connection#prepareStatement(String, int)}). + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, it will be + * released when the session is closed or disconnected. */ public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys); /** * Get a non-batchable prepared statement to use for inserting / deleting / updating. * using JDBC3 getGeneratedKeys ({@link java.sql.Connection#prepareStatement(String, String[])}). + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, it will be + * released when the session is closed or disconnected. */ public PreparedStatement prepareStatement(String sql, String[] columnNames); /** * Get a non-batchable prepared statement to use for selecting. Does not * result in execution of the current batch. + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, + * it will be released when the session is closed or disconnected. */ public PreparedStatement prepareSelectStatement(String sql); /** * Get a non-batchable prepared statement to use for inserting / deleting / updating. + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, it will be + * released when the session is closed or disconnected. */ public PreparedStatement prepareStatement(String sql, boolean isCallable); /** * Get a non-batchable callable statement to use for inserting / deleting / updating. + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, it will be + * released when the session is closed or disconnected. */ public CallableStatement prepareCallableStatement(String sql); @@ -161,28 +180,34 @@ public interface ConnectionManager extends Serializable { * (might be called many times before a single call to executeBatch()). * After setting parameters, call addToBatch - do not execute the * statement explicitly. - * @see org.hibernate.jdbc.Batcher#addToBatch + * @see org.hibernate.engine.jdbc.batch.spi.Batch#addToBatch + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, it will be + * released when the session is closed or disconnected. */ - public PreparedStatement prepareBatchStatement(String sql, boolean isCallable); + public PreparedStatement prepareBatchStatement(Object key, String sql, boolean isCallable); /** - * Get a prepared statement for use in loading / querying. If not explicitly - * released by closeQueryStatement(), it will be released when the - * session is closed or disconnected. + * Get a prepared statement for use in loading / querying. Does not + * result in execution of the current batch. + *

+ * If not explicitly closed via {@link java.sql.PreparedStatement#close()}, + * it will be released when the session is closed or disconnected. */ public PreparedStatement prepareQueryStatement( String sql, boolean isScrollable, ScrollMode scrollMode, boolean isCallable); + /** * Cancel the current query statement */ public void cancelLastQuery(); - public void abortBatch(SQLException sqle); + public void abortBatch(); - public void addToBatch(Expectation expectation ); + public void addToBatch(Object batchKey, String sql, Expectation expectation); public void executeBatch(); } diff --git a/hibernate-core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java index 703b81deed..ec43d984ef 100644 --- a/hibernate-core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java @@ -99,7 +99,6 @@ import org.hibernate.event.EventListeners; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.UUIDGenerator; import org.hibernate.id.factory.IdentifierGeneratorFactory; -import org.hibernate.jdbc.BatcherFactory; import org.hibernate.mapping.Collection; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.RootClass; @@ -1235,10 +1234,6 @@ public final class SessionFactoryImpl implements SessionFactory, SessionFactoryI return filters.keySet(); } - public BatcherFactory getBatcherFactory() { - return settings.getBatcherFactory(); - } - public IdentifierGenerator getIdentifierGenerator(String rootEntityName) { return (IdentifierGenerator) identifierGenerators.get(rootEntityName); } diff --git a/hibernate-core/src/main/java/org/hibernate/jdbc/AbstractBatcher.java b/hibernate-core/src/main/java/org/hibernate/jdbc/AbstractBatcher.java deleted file mode 100644 index d619475881..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/jdbc/AbstractBatcher.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Middleware LLC. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - * - */ -package org.hibernate.jdbc; - -import java.sql.PreparedStatement; -import java.sql.SQLException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.hibernate.HibernateException; -import org.hibernate.engine.jdbc.spi.SQLExceptionHelper; - -/** - * Manages prepared statements and batching. - * - * @author Gavin King - */ -public abstract class AbstractBatcher implements Batcher { - - protected static final Logger log = LoggerFactory.getLogger( AbstractBatcher.class ); - - private final SQLExceptionHelper exceptionHelper; - private final int jdbcBatchSize; - - private PreparedStatement batchUpdate; - private String batchUpdateSQL; - private boolean isClosingBatchUpdate = false; - - public AbstractBatcher(SQLExceptionHelper exceptionHelper, int jdbcBatchSize) { - this.exceptionHelper = exceptionHelper; - this.jdbcBatchSize = jdbcBatchSize; - } - - public final int getJdbcBatchSize() { - return jdbcBatchSize; - } - - public boolean hasOpenResources() { - try { - return !isClosingBatchUpdate && batchUpdate != null && ! batchUpdate.isClosed(); - } - catch (SQLException sqle) { - throw exceptionHelper.convert( - sqle, - "Could check to see if batch statement was closed", - batchUpdateSQL - ); - } - } - - public PreparedStatement getStatement(String sql) { - return batchUpdate != null && batchUpdateSQL.equals( sql ) ? batchUpdate : null; - } - - public void setStatement(String sql, PreparedStatement ps) { - checkNotClosingBatchUpdate(); - batchUpdateSQL = sql; - batchUpdate = ps; - } - - protected PreparedStatement getStatement() { - return batchUpdate; - } - - public void abortBatch(SQLException sqle) { - closeStatements(); - } - - /** - * Actually releases the batcher, allowing it to cleanup internally held - * resources. - */ - public void closeStatements() { - try { - closeBatchUpdate(); - } - catch ( SQLException sqle ) { - //no big deal - log.warn( "Could not close a JDBC prepared statement", sqle ); - } - batchUpdate = null; - batchUpdateSQL = null; - } - - public void executeBatch() throws HibernateException { - checkNotClosingBatchUpdate(); - if (batchUpdate!=null) { - try { - try { - doExecuteBatch(batchUpdate); - } - finally { - closeBatchUpdate(); - } - } - catch (SQLException sqle) { - throw exceptionHelper.convert( - sqle, - "Could not execute JDBC batch update", - batchUpdateSQL - ); - } - finally { - batchUpdate=null; - batchUpdateSQL=null; - } - } - } - - protected abstract void doExecuteBatch(PreparedStatement ps) throws SQLException, HibernateException; - - - private void closeBatchUpdate() throws SQLException{ - checkNotClosingBatchUpdate(); - try { - if ( batchUpdate != null ) { - isClosingBatchUpdate = true; - batchUpdate.close(); - } - } - finally { - isClosingBatchUpdate = false; - } - - } - - private void checkNotClosingBatchUpdate() { - if ( isClosingBatchUpdate ) { - throw new IllegalStateException( "Cannot perform operation while closing batch update." ); - } - } -} - - - - - - diff --git a/hibernate-core/src/main/java/org/hibernate/jdbc/Batcher.java b/hibernate-core/src/main/java/org/hibernate/jdbc/Batcher.java deleted file mode 100644 index df96c984e7..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/jdbc/Batcher.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Middleware LLC. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - * - */ -package org.hibernate.jdbc; - -import java.sql.CallableStatement; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; - -import org.hibernate.HibernateException; -import org.hibernate.ScrollMode; -import org.hibernate.dialect.Dialect; - -/** - * Manages PreparedStatements for a session. Abstracts JDBC - * batching to maintain the illusion that a single logical batch - * exists for the whole session, even when batching is disabled. - * Provides transparent PreparedStatement caching. - * - * @see java.sql.PreparedStatement - * @see org.hibernate.impl.SessionImpl - * @author Gavin King - */ -public interface Batcher { - - public PreparedStatement getStatement(String sql); - public void setStatement(String sql, PreparedStatement ps); - public boolean hasOpenResources(); - - /** - * Add an insert / delete / update to the current batch (might be called multiple times - * for single prepareBatchStatement()) - */ - public void addToBatch(Expectation expectation) throws SQLException, HibernateException; - - /** - * Execute the batch - */ - public void executeBatch() throws HibernateException; - - /** - * Must be called when an exception occurs - * @param sqle the (not null) exception that is the reason for aborting - */ - public void abortBatch(SQLException sqle); - - /** - * Actually releases the batcher, allowing it to cleanup internally held - * resources. - */ - public void closeStatements(); -} - diff --git a/hibernate-core/src/main/java/org/hibernate/jdbc/BatcherFactory.java b/hibernate-core/src/main/java/org/hibernate/jdbc/BatcherFactory.java deleted file mode 100755 index bf03718847..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/jdbc/BatcherFactory.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Middleware LLC. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - * - */ -package org.hibernate.jdbc; - -import org.hibernate.Interceptor; -import org.hibernate.engine.jdbc.spi.SQLExceptionHelper; - - -/** - * Factory for Batcher instances. - * @author Gavin King - */ -public interface BatcherFactory { - public void setJdbcBatchSize(int jdbcBatchSize); - public Batcher createBatcher(SQLExceptionHelper exceptionHelper); -} diff --git a/hibernate-core/src/main/java/org/hibernate/jdbc/BatchingBatcher.java b/hibernate-core/src/main/java/org/hibernate/jdbc/BatchingBatcher.java deleted file mode 100644 index aae2972d32..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/jdbc/BatchingBatcher.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Middleware LLC. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - * - */ -package org.hibernate.jdbc; - -import java.sql.PreparedStatement; -import java.sql.SQLException; - -import org.hibernate.HibernateException; -import org.hibernate.engine.jdbc.spi.SQLExceptionHelper; - -/** - * An implementation of the Batcher interface that - * actually uses batching - * @author Gavin King - */ -public class BatchingBatcher extends AbstractBatcher { - - private Expectation[] expectations; - - private int currentSize; - public BatchingBatcher(SQLExceptionHelper exceptionHelper, int jdbcBatchSize) { - super( exceptionHelper, jdbcBatchSize ); - expectations = new Expectation[ jdbcBatchSize ]; - currentSize = 0; - } - - public void addToBatch(Expectation expectation) throws SQLException, HibernateException { - if ( !expectation.canBeBatched() ) { - throw new HibernateException( "attempting to batch an operation which cannot be batched" ); - } - PreparedStatement batchUpdate = getStatement(); - batchUpdate.addBatch(); - expectations[ currentSize++ ] = expectation; - if ( currentSize == getJdbcBatchSize() ) { - doExecuteBatch( batchUpdate ); - } - } - - protected void doExecuteBatch(PreparedStatement ps) throws SQLException, HibernateException { - if ( currentSize == 0 ) { - log.debug( "no batched statements to execute" ); - } - else { - if ( log.isDebugEnabled() ) { - log.debug( "Executing batch size: " + currentSize ); - } - - try { - checkRowCounts( ps.executeBatch(), ps ); - } - catch (RuntimeException re) { - log.error( "Exception executing batch: ", re ); - throw re; - } - finally { - currentSize = 0; - } - - } - } - - private void checkRowCounts(int[] rowCounts, PreparedStatement ps) throws SQLException, HibernateException { - int numberOfRowCounts = rowCounts.length; - if ( numberOfRowCounts != currentSize ) { - log.warn( "JDBC driver did not return the expected number of row counts" ); - } - for ( int i = 0; i < numberOfRowCounts; i++ ) { - expectations[i].verifyOutcome( rowCounts[i], ps, i ); - } - } - -} - - - - - - diff --git a/hibernate-core/src/main/java/org/hibernate/jdbc/BatchingBatcherFactory.java b/hibernate-core/src/main/java/org/hibernate/jdbc/BatchingBatcherFactory.java deleted file mode 100755 index a1e14dbc0b..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/jdbc/BatchingBatcherFactory.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Middleware LLC. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - * - */ -package org.hibernate.jdbc; - -import org.hibernate.engine.jdbc.spi.SQLExceptionHelper; - - -/** - * A BatcherFactory implementation which constructs Batcher instances - * capable of actually performing batch operations. - * - * @author Gavin King - */ -public class BatchingBatcherFactory implements BatcherFactory { - - private int jdbcBatchSize; - - public void setJdbcBatchSize(int jdbcBatchSize) { - this.jdbcBatchSize = jdbcBatchSize; - } - - public Batcher createBatcher(SQLExceptionHelper exceptionHelper) { - return new BatchingBatcher( exceptionHelper, jdbcBatchSize ); - } - -} diff --git a/hibernate-core/src/main/java/org/hibernate/jdbc/NonBatchingBatcher.java b/hibernate-core/src/main/java/org/hibernate/jdbc/NonBatchingBatcher.java deleted file mode 100644 index 6dfea07339..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/jdbc/NonBatchingBatcher.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Middleware LLC. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - * - */ -package org.hibernate.jdbc; - -import java.sql.PreparedStatement; -import java.sql.SQLException; - -import org.hibernate.HibernateException; -import org.hibernate.engine.jdbc.spi.SQLExceptionHelper; - -/** - * An implementation of the Batcher interface that does no batching - * - * @author Gavin King - */ -public class NonBatchingBatcher extends AbstractBatcher { - - public NonBatchingBatcher(SQLExceptionHelper exceptionHelper) { - super( exceptionHelper, 1 ); - } - - public void addToBatch(Expectation expectation) throws SQLException, HibernateException { - PreparedStatement statement = getStatement(); - final int rowCount = statement.executeUpdate(); - expectation.verifyOutcome( rowCount, statement, 0 ); - } - - protected void doExecuteBatch(PreparedStatement ps) throws SQLException, HibernateException { - } - -} diff --git a/hibernate-core/src/main/java/org/hibernate/jdbc/NonBatchingBatcherFactory.java b/hibernate-core/src/main/java/org/hibernate/jdbc/NonBatchingBatcherFactory.java deleted file mode 100755 index ee55421e0d..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/jdbc/NonBatchingBatcherFactory.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Middleware LLC. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - * - */ -package org.hibernate.jdbc; - -import org.hibernate.AssertionFailure; -import org.hibernate.engine.jdbc.spi.SQLExceptionHelper; - - -/** - * A BatcherFactory implementation which constructs Batcher instances - * that do not perform batch operations. - * - * @author Gavin King - */ -public class NonBatchingBatcherFactory implements BatcherFactory { - - public void setJdbcBatchSize(int jdbcBatchSize) { - if ( jdbcBatchSize > 1 ) { - throw new AssertionFailure( "jdbcBatchSize must be 1 for " + getClass().getName() ); - } - } - - public Batcher createBatcher(SQLExceptionHelper exceptionHelper) { - return new NonBatchingBatcher( exceptionHelper ); - } - -} diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java index d297f6e1a0..cabd43a6bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java @@ -1083,7 +1083,7 @@ public abstract class AbstractCollectionPersister boolean useBatch = expectation.canBeBatched(); String sql = getSQLDeleteString(); if ( useBatch ) { - st = session.getJDBCContext().getConnectionManager().prepareBatchStatement( sql, callable ); + st = session.getJDBCContext().getConnectionManager().prepareBatchStatement( this, sql, callable ); } else { st = session.getJDBCContext().getConnectionManager().prepareStatement( sql, callable ); @@ -1095,7 +1095,7 @@ public abstract class AbstractCollectionPersister writeKey( st, id, offset, session ); if ( useBatch ) { - session.getJDBCContext().getConnectionManager().addToBatch( expectation ); + session.getJDBCContext().getConnectionManager().addToBatch( this, sql, expectation ); } else { expectation.verifyOutcome( st.executeUpdate(), st, -1 ); @@ -1103,7 +1103,7 @@ public abstract class AbstractCollectionPersister } catch ( SQLException sqle ) { if ( useBatch ) { - session.getJDBCContext().getConnectionManager().abortBatch( sqle ); + session.getJDBCContext().getConnectionManager().abortBatch(); } throw sqle; } @@ -1161,7 +1161,9 @@ public abstract class AbstractCollectionPersister String sql = getSQLInsertRowString(); if ( useBatch ) { - st = session.getJDBCContext().getConnectionManager().prepareBatchStatement( sql, callable ); + st = session.getJDBCContext().getConnectionManager().prepareBatchStatement( + this, sql, callable + ); } else { st = session.getJDBCContext().getConnectionManager().prepareStatement( sql, callable ); @@ -1182,7 +1184,7 @@ public abstract class AbstractCollectionPersister loc = writeElement(st, collection.getElement(entry), loc, session ); if ( useBatch ) { - session.getJDBCContext().getConnectionManager().addToBatch( expectation ); + session.getJDBCContext().getConnectionManager().addToBatch( this, sql, expectation ); } else { expectation.verifyOutcome( st.executeUpdate(), st, -1 ); @@ -1193,7 +1195,7 @@ public abstract class AbstractCollectionPersister } catch ( SQLException sqle ) { if ( useBatch ) { - session.getJDBCContext().getConnectionManager().abortBatch( sqle ); + session.getJDBCContext().getConnectionManager().abortBatch(); } throw sqle; } @@ -1261,7 +1263,9 @@ public abstract class AbstractCollectionPersister String sql = getSQLDeleteRowString(); if ( useBatch ) { - st = session.getJDBCContext().getConnectionManager().prepareBatchStatement( sql, callable ); + st = session.getJDBCContext().getConnectionManager().prepareBatchStatement( + this, sql, callable + ); } else { st = session.getJDBCContext().getConnectionManager().prepareStatement( sql, callable ); @@ -1286,7 +1290,7 @@ public abstract class AbstractCollectionPersister } if ( useBatch ) { - session.getJDBCContext().getConnectionManager().addToBatch( expectation ); + session.getJDBCContext().getConnectionManager().addToBatch( this, sql, expectation ); } else { expectation.verifyOutcome( st.executeUpdate(), st, -1 ); @@ -1295,7 +1299,7 @@ public abstract class AbstractCollectionPersister } catch ( SQLException sqle ) { if ( useBatch ) { - session.getJDBCContext().getConnectionManager().abortBatch( sqle ); + session.getJDBCContext().getConnectionManager().abortBatch(); } throw sqle; } @@ -1361,7 +1365,9 @@ public abstract class AbstractCollectionPersister if ( useBatch ) { if ( st == null ) { - st = session.getJDBCContext().getConnectionManager().prepareBatchStatement( sql, callable ); + st = session.getJDBCContext().getConnectionManager().prepareBatchStatement( + this, sql, callable + ); } } else { @@ -1381,7 +1387,7 @@ public abstract class AbstractCollectionPersister writeElement(st, collection.getElement(entry), offset, session ); if ( useBatch ) { - session.getJDBCContext().getConnectionManager().addToBatch( expectation ); + session.getJDBCContext().getConnectionManager().addToBatch( this, sql, expectation ); } else { expectation.verifyOutcome( st.executeUpdate(), st, -1 ); @@ -1391,7 +1397,7 @@ public abstract class AbstractCollectionPersister } catch ( SQLException sqle ) { if ( useBatch ) { - session.getJDBCContext().getConnectionManager().abortBatch( sqle ); + session.getJDBCContext().getConnectionManager().abortBatch(); } throw sqle; } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java index 1a2184cac9..4fa624149b 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java @@ -211,7 +211,9 @@ public class BasicCollectionPersister extends AbstractCollectionPersister { if ( useBatch ) { if ( st == null ) { - st = session.getJDBCContext().getConnectionManager().prepareBatchStatement( sql, callable ); + st = session.getJDBCContext().getConnectionManager().prepareBatchStatement( + this, sql, callable + ); } } else { @@ -235,7 +237,7 @@ public class BasicCollectionPersister extends AbstractCollectionPersister { } if ( useBatch ) { - session.getJDBCContext().getConnectionManager().addToBatch( expectation ); + session.getJDBCContext().getConnectionManager().addToBatch( this, sql, expectation ); } else { expectation.verifyOutcome( st.executeUpdate(), st, -1 ); @@ -243,7 +245,7 @@ public class BasicCollectionPersister extends AbstractCollectionPersister { } catch ( SQLException sqle ) { if ( useBatch ) { - session.getJDBCContext().getConnectionManager().abortBatch( sqle ); + session.getJDBCContext().getConnectionManager().abortBatch(); } throw sqle; } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java index 5045323364..2057a92cd6 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java @@ -196,27 +196,26 @@ public class OneToManyPersister extends AbstractCollectionPersister { Object entry = entries.next(); if ( collection.needsUpdating( entry, i, elementType ) ) { // will still be issued when it used to be null + String sql = getSQLDeleteRowString(); if ( st == null ) { - String sql = getSQLDeleteRowString(); if ( isDeleteCallable() ) { expectation = Expectations.appropriateExpectation( getDeleteCheckStyle() ); useBatch = expectation.canBeBatched(); st = useBatch - ? session.getJDBCContext().getConnectionManager().prepareBatchStatement( sql, true ) + ? session.getJDBCContext().getConnectionManager().prepareBatchStatement( this, sql, true ) : session.getJDBCContext().getConnectionManager().prepareStatement( sql, true ); offset += expectation.prepare( st ); } else { st = session.getJDBCContext().getConnectionManager().prepareBatchStatement( - getSQLDeleteRowString(), - false + this, sql, false ); } } int loc = writeKey( st, id, offset, session ); writeElementToWhere( st, collection.getSnapshotElement(entry, i), loc, session ); if ( useBatch ) { - session.getJDBCContext().getConnectionManager().addToBatch( expectation ); + session.getJDBCContext().getConnectionManager().addToBatch( this, sql, expectation ); } else { expectation.verifyOutcome( st.executeUpdate(), st, -1 ); @@ -228,7 +227,7 @@ public class OneToManyPersister extends AbstractCollectionPersister { } catch ( SQLException sqle ) { if ( useBatch ) { - session.getJDBCContext().getConnectionManager().abortBatch( sqle ); + session.getJDBCContext().getConnectionManager().abortBatch(); } throw sqle; } @@ -255,7 +254,9 @@ public class OneToManyPersister extends AbstractCollectionPersister { if ( collection.needsUpdating( entry, i, elementType ) ) { if ( useBatch ) { if ( st == null ) { - st = session.getJDBCContext().getConnectionManager().prepareBatchStatement( sql, callable ); + st = session.getJDBCContext().getConnectionManager().prepareBatchStatement( + this, sql, callable + ); } } else { @@ -272,7 +273,7 @@ public class OneToManyPersister extends AbstractCollectionPersister { writeElementToWhere( st, collection.getElement( entry ), loc, session ); if ( useBatch ) { - session.getJDBCContext().getConnectionManager().addToBatch( expectation ); + session.getJDBCContext().getConnectionManager().addToBatch( this, sql, expectation ); } else { expectation.verifyOutcome( st.executeUpdate(), st, -1 ); @@ -284,7 +285,7 @@ public class OneToManyPersister extends AbstractCollectionPersister { } catch ( SQLException sqle ) { if ( useBatch ) { - session.getJDBCContext().getConnectionManager().abortBatch( sqle ); + session.getJDBCContext().getConnectionManager().abortBatch(); } throw sqle; } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 528f412472..ba5e71e7bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -2381,7 +2381,7 @@ public abstract class AbstractEntityPersister // Render the SQL query final PreparedStatement insert; if ( useBatch ) { - insert = session.getJDBCContext().getConnectionManager().prepareBatchStatement( sql, callable ); + insert = session.getJDBCContext().getConnectionManager().prepareBatchStatement( this, sql, callable ); } else { insert = session.getJDBCContext().getConnectionManager().prepareStatement( sql, callable ); @@ -2398,7 +2398,7 @@ public abstract class AbstractEntityPersister if ( useBatch ) { // TODO : shouldnt inserts be Expectations.NONE? - session.getJDBCContext().getConnectionManager().addToBatch( expectation ); + session.getJDBCContext().getConnectionManager().addToBatch( this, sql, expectation ); } else { expectation.verifyOutcome( insert.executeUpdate(), insert, -1 ); @@ -2407,7 +2407,7 @@ public abstract class AbstractEntityPersister } catch ( SQLException sqle ) { if ( useBatch ) { - session.getJDBCContext().getConnectionManager().abortBatch( sqle ); + session.getJDBCContext().getConnectionManager().abortBatch(); } throw sqle; } @@ -2500,7 +2500,7 @@ public abstract class AbstractEntityPersister int index = 1; // starting index final PreparedStatement update; if ( useBatch ) { - update = session.getJDBCContext().getConnectionManager().prepareBatchStatement( sql, callable ); + update = session.getJDBCContext().getConnectionManager().prepareBatchStatement( this, sql, callable ); } else { update = session.getJDBCContext().getConnectionManager().prepareStatement( sql, callable ); @@ -2543,7 +2543,7 @@ public abstract class AbstractEntityPersister } if ( useBatch ) { - session.getJDBCContext().getConnectionManager().addToBatch( expectation ); + session.getJDBCContext().getConnectionManager().addToBatch( this, sql, expectation ); return true; } else { @@ -2553,7 +2553,7 @@ public abstract class AbstractEntityPersister } catch ( SQLException sqle ) { if ( useBatch ) { - session.getJDBCContext().getConnectionManager().abortBatch( sqle ); + session.getJDBCContext().getConnectionManager().abortBatch(); } throw sqle; } @@ -2614,7 +2614,7 @@ public abstract class AbstractEntityPersister PreparedStatement delete; int index = 1; if ( useBatch ) { - delete = session.getJDBCContext().getConnectionManager().prepareBatchStatement( sql, callable ); + delete = session.getJDBCContext().getConnectionManager().prepareBatchStatement( this, sql, callable ); } else { delete = session.getJDBCContext().getConnectionManager().prepareStatement( sql, callable ); @@ -2649,7 +2649,7 @@ public abstract class AbstractEntityPersister } if ( useBatch ) { - session.getJDBCContext().getConnectionManager().addToBatch( expectation ); + session.getJDBCContext().getConnectionManager().addToBatch( this, sql, expectation ); } else { check( delete.executeUpdate(), id, j, expectation, delete ); @@ -2658,7 +2658,7 @@ public abstract class AbstractEntityPersister } catch ( SQLException sqle ) { if ( useBatch ) { - session.getJDBCContext().getConnectionManager().abortBatch( sqle ); + session.getJDBCContext().getConnectionManager().abortBatch(); } throw sqle; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/batch/BatchTest.java b/hibernate-core/src/test/java/org/hibernate/test/batch/BatchTest.java index 4e4df6c734..2bb8158b79 100755 --- a/hibernate-core/src/test/java/org/hibernate/test/batch/BatchTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/batch/BatchTest.java @@ -10,6 +10,7 @@ import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hibernate.Session; import org.hibernate.Transaction; +import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.testing.junit.functional.FunctionalTestCase; import org.hibernate.testing.junit.functional.FunctionalTestClassTestSuite; import org.hibernate.cfg.Configuration; @@ -49,16 +50,37 @@ public class BatchTest extends FunctionalTestCase { final int N = 5000; //26 secs with batch flush, 26 without //final int N = 100000; //53 secs with batch flush, OOME without //final int N = 250000; //137 secs with batch flush, OOME without + int batchSize = ( ( SessionFactoryImplementor ) getSessions() ).getSettings().getJdbcBatchSize(); + doBatchInsertUpdate( N, batchSize ); + System.out.println( System.currentTimeMillis() - start ); + } + public void testBatchInsertUpdateSizeEqJdbcBatchSize() { + int batchSize = ( ( SessionFactoryImplementor ) getSessions() ).getSettings().getJdbcBatchSize(); + doBatchInsertUpdate( 50, batchSize ); + } + + public void testBatchInsertUpdateSizeLtJdbcBatchSize() { + int batchSize = ( ( SessionFactoryImplementor ) getSessions() ).getSettings().getJdbcBatchSize(); + doBatchInsertUpdate( 50, batchSize - 1 ); + } + + public void testBatchInsertUpdateSizeGtJdbcBatchSize() { + long start = System.currentTimeMillis(); + int batchSize = ( ( SessionFactoryImplementor ) getSessions() ).getSettings().getJdbcBatchSize(); + doBatchInsertUpdate( 50, batchSize + 1 ); + } + + public void doBatchInsertUpdate(int nEntities, int nBeforeFlush) { Session s = openSession(); s.setCacheMode( CacheMode.IGNORE ); Transaction t = s.beginTransaction(); - for ( int i = 0; i < N; i++ ) { + for ( int i = 0; i < nEntities; i++ ) { DataPoint dp = new DataPoint(); dp.setX( new BigDecimal( i * 0.1d ).setScale( 19, BigDecimal.ROUND_DOWN ) ); dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale( 19, BigDecimal.ROUND_DOWN ) ); s.save( dp ); - if ( i % 20 == 0 ) { + if ( i + 1 % nBeforeFlush == 0 ) { s.flush(); s.clear(); } @@ -75,15 +97,30 @@ public class BatchTest extends FunctionalTestCase { while ( sr.next() ) { DataPoint dp = ( DataPoint ) sr.get( 0 ); dp.setDescription( "done!" ); - if ( ++i % 20 == 0 ) { + if ( ++i % nBeforeFlush == 0 ) { s.flush(); s.clear(); } } t.commit(); s.close(); - System.out.println( System.currentTimeMillis() - start ); - } + s = openSession(); + s.setCacheMode( CacheMode.IGNORE ); + t = s.beginTransaction(); + i = 0; + sr = s.createQuery( "from DataPoint dp order by dp.x asc" ) + .scroll( ScrollMode.FORWARD_ONLY ); + while ( sr.next() ) { + DataPoint dp = ( DataPoint ) sr.get( 0 ); + s.delete( dp ); + if ( ++i % nBeforeFlush == 0 ) { + s.flush(); + s.clear(); + } + } + t.commit(); + s.close(); + } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/insertordering/InsertOrderingTest.java b/hibernate-core/src/test/java/org/hibernate/test/insertordering/InsertOrderingTest.java index 1a207b62f7..4df42c0a47 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/insertordering/InsertOrderingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/insertordering/InsertOrderingTest.java @@ -3,22 +3,21 @@ package org.hibernate.test.insertordering; import java.util.List; import java.util.ArrayList; import java.util.Iterator; -import java.sql.SQLException; import java.sql.PreparedStatement; import junit.framework.Test; +import org.hibernate.engine.jdbc.batch.internal.BatchBuilder; +import org.hibernate.engine.jdbc.batch.internal.BatchingBatch; +import org.hibernate.engine.jdbc.batch.spi.Batch; import org.hibernate.engine.jdbc.spi.SQLExceptionHelper; +import org.hibernate.engine.jdbc.spi.SQLStatementLogger; import org.hibernate.testing.junit.functional.FunctionalTestCase; import org.hibernate.testing.junit.functional.FunctionalTestClassTestSuite; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; import org.hibernate.Session; -import org.hibernate.HibernateException; -import org.hibernate.jdbc.BatchingBatcher; import org.hibernate.jdbc.Expectation; -import org.hibernate.jdbc.BatcherFactory; -import org.hibernate.jdbc.Batcher; /** * {@inheritDoc} @@ -42,7 +41,7 @@ public class InsertOrderingTest extends FunctionalTestCase { super.configure( cfg ); cfg.setProperty( Environment.ORDER_INSERTS, "true" ); cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "10" ); - cfg.setProperty( Environment.BATCH_STRATEGY, StatsBatcherFactory.class.getName() ); + cfg.setProperty( Environment.BATCH_STRATEGY, StatsBatchBuilder.class.getName() ); } public void testBatchOrdering() { @@ -56,11 +55,11 @@ public class InsertOrderingTest extends FunctionalTestCase { s.save( group ); user.addMembership( group ); } - StatsBatcher.reset(); + StatsBatch.reset(); s.getTransaction().commit(); s.close(); - assertEquals( 3, StatsBatcher.batchSizes.size() ); + assertEquals( 3, StatsBatch.batchSizes.size() ); s = openSession(); s.beginTransaction(); @@ -76,13 +75,13 @@ public class InsertOrderingTest extends FunctionalTestCase { public int count = 0; } - public static class StatsBatcher extends BatchingBatcher { + public static class StatsBatch extends BatchingBatch { private static String batchSQL; private static List batchSizes = new ArrayList(); private static int currentBatch = -1; - public StatsBatcher(SQLExceptionHelper exceptionHelper, int jdbcBatchSize) { - super( exceptionHelper, jdbcBatchSize ); + public StatsBatch(Object key, SQLStatementLogger statementLogger, SQLExceptionHelper exceptionHelper, int jdbcBatchSize) { + super( key, statementLogger, exceptionHelper, jdbcBatchSize ); } static void reset() { @@ -91,7 +90,7 @@ public class InsertOrderingTest extends FunctionalTestCase { batchSQL = null; } - public void setStatement(String sql, PreparedStatement ps) { + public void addBatchStatement(Object key, String sql, PreparedStatement ps) { if ( batchSQL == null || ! batchSQL.equals( sql ) ) { currentBatch++; batchSQL = sql; @@ -99,31 +98,31 @@ public class InsertOrderingTest extends FunctionalTestCase { System.out.println( "--------------------------------------------------------" ); System.out.println( "Preparing statement [" + sql + "]" ); } - super.setStatement( sql, ps ); + super.addBatchStatement( key, sql, ps ); } - public void addToBatch(Expectation expectation) throws SQLException, HibernateException { + public void addToBatch(Object key, String sql, Expectation expectation) { Counter counter = ( Counter ) batchSizes.get( currentBatch ); counter.count++; System.out.println( "Adding to batch [" + batchSQL + "]" ); - super.addToBatch( expectation ); + super.addToBatch( key, sql, expectation ); } - protected void doExecuteBatch(PreparedStatement ps) throws SQLException, HibernateException { + protected void doExecuteBatch() { System.out.println( "executing batch [" + batchSQL + "]" ); System.out.println( "--------------------------------------------------------" ); - super.doExecuteBatch( ps ); + super.doExecuteBatch(); } } - public static class StatsBatcherFactory implements BatcherFactory { + public static class StatsBatchBuilder extends BatchBuilder { private int jdbcBatchSize; public void setJdbcBatchSize(int jdbcBatchSize) { this.jdbcBatchSize = jdbcBatchSize; } - public Batcher createBatcher(SQLExceptionHelper exceptionHelper) { - return new StatsBatcher( exceptionHelper, jdbcBatchSize ); + public Batch buildBatch(Object key, SQLStatementLogger statementLogger, SQLExceptionHelper exceptionHelper) { + return new StatsBatch(key, statementLogger, exceptionHelper, jdbcBatchSize ); } } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/AggressiveReleaseTest.java b/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/AggressiveReleaseTest.java index 2a893a8fd2..e32122b4ed 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/AggressiveReleaseTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/AggressiveReleaseTest.java @@ -35,7 +35,6 @@ import org.hibernate.ConnectionReleaseMode; import org.hibernate.engine.jdbc.internal.LogicalConnectionImpl; import org.hibernate.engine.jdbc.spi.ConnectionObserver; import org.hibernate.engine.jdbc.internal.proxy.ProxyBuilder; -import org.hibernate.jdbc.NonBatchingBatcherFactory; import org.hibernate.test.common.BasicTestingJdbcServiceImpl; import org.hibernate.testing.junit.UnitTestCase; @@ -135,8 +134,7 @@ public class AggressiveReleaseTest extends UnitTestCase { null, ConnectionReleaseMode.AFTER_STATEMENT, services, - null, - new NonBatchingBatcherFactory() + null ); Connection proxiedConnection = ProxyBuilder.buildConnection( logicalConnection ); ConnectionCounter observer = new ConnectionCounter(); @@ -170,8 +168,7 @@ public class AggressiveReleaseTest extends UnitTestCase { null, ConnectionReleaseMode.AFTER_STATEMENT, services, - null, - new NonBatchingBatcherFactory() + null ); Connection proxiedConnection = ProxyBuilder.buildConnection( logicalConnection ); ConnectionCounter observer = new ConnectionCounter(); @@ -230,8 +227,7 @@ public class AggressiveReleaseTest extends UnitTestCase { null, ConnectionReleaseMode.AFTER_STATEMENT, services, - null, - new NonBatchingBatcherFactory() + null ); Connection proxiedConnection = ProxyBuilder.buildConnection( logicalConnection ); ConnectionCounter observer = new ConnectionCounter(); diff --git a/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/BasicConnectionProxyTest.java b/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/BasicConnectionProxyTest.java index 7de8b8f8ef..8107c1ea0b 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/BasicConnectionProxyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/jdbc/proxies/BasicConnectionProxyTest.java @@ -34,7 +34,6 @@ import org.hibernate.ConnectionReleaseMode; import org.hibernate.JDBCException; import org.hibernate.engine.jdbc.internal.LogicalConnectionImpl; import org.hibernate.engine.jdbc.internal.proxy.ProxyBuilder; -import org.hibernate.jdbc.NonBatchingBatcherFactory; import org.hibernate.test.common.BasicTestingJdbcServiceImpl; import org.hibernate.testing.junit.UnitTestCase; @@ -63,8 +62,7 @@ public class BasicConnectionProxyTest extends UnitTestCase { null, ConnectionReleaseMode.AFTER_TRANSACTION, services, - null, - new NonBatchingBatcherFactory() + null ); Connection proxiedConnection = ProxyBuilder.buildConnection( logicalConnection ); try { @@ -92,8 +90,7 @@ public class BasicConnectionProxyTest extends UnitTestCase { null, ConnectionReleaseMode.AFTER_TRANSACTION, services, - null, - new NonBatchingBatcherFactory() + null ); Connection proxiedConnection = ProxyBuilder.buildConnection( logicalConnection ); try { @@ -115,8 +112,7 @@ public class BasicConnectionProxyTest extends UnitTestCase { null, ConnectionReleaseMode.AFTER_TRANSACTION, services, - null, - new NonBatchingBatcherFactory() + null ); Connection proxiedConnection = ProxyBuilder.buildConnection( logicalConnection ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/manytomany/batchload/BatchedManyToManyTest.java b/hibernate-core/src/test/java/org/hibernate/test/manytomany/batchload/BatchedManyToManyTest.java index 8c71891ac3..ca66ad9aa1 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/manytomany/batchload/BatchedManyToManyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/manytomany/batchload/BatchedManyToManyTest.java @@ -26,7 +26,11 @@ import java.util.List; import junit.framework.Test; import junit.framework.Assert; +import org.hibernate.engine.jdbc.batch.internal.BatchBuilder; +import org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch; +import org.hibernate.engine.jdbc.batch.spi.Batch; import org.hibernate.engine.jdbc.spi.SQLExceptionHelper; +import org.hibernate.engine.jdbc.spi.SQLStatementLogger; import org.hibernate.testing.junit.functional.FunctionalTestCase; import org.hibernate.testing.junit.functional.FunctionalTestClassTestSuite; import org.hibernate.cfg.Configuration; @@ -35,9 +39,6 @@ import org.hibernate.Session; import org.hibernate.Hibernate; import org.hibernate.Interceptor; import org.hibernate.EmptyInterceptor; -import org.hibernate.jdbc.BatcherFactory; -import org.hibernate.jdbc.NonBatchingBatcher; -import org.hibernate.jdbc.Batcher; import org.hibernate.stat.CollectionStatistics; import org.hibernate.loader.collection.BatchingCollectionInitializer; import org.hibernate.persister.collection.AbstractCollectionPersister; @@ -64,23 +65,23 @@ public class BatchedManyToManyTest extends FunctionalTestCase { public void configure(Configuration cfg) { cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "false" ); cfg.setProperty( Environment.GENERATE_STATISTICS, "true" ); - cfg.setProperty( Environment.BATCH_STRATEGY, TestingBatcherFactory.class.getName() ); + cfg.setProperty( Environment.BATCH_STRATEGY, TestingBatchBuilder.class.getName() ); } - public static class TestingBatcherFactory implements BatcherFactory { + public static class TestingBatchBuilder extends BatchBuilder { private int jdbcBatchSize; public void setJdbcBatchSize(int jdbcBatchSize) { this.jdbcBatchSize = jdbcBatchSize; } - public Batcher createBatcher(SQLExceptionHelper exceptionHelper) { - return new TestingBatcher( exceptionHelper, jdbcBatchSize ); + public Batch buildBatch(Object key, SQLStatementLogger statementLogger, SQLExceptionHelper exceptionHelper) { + return new TestingBatch(key, statementLogger, exceptionHelper, jdbcBatchSize ); } } - public static class TestingBatcher extends NonBatchingBatcher { - public TestingBatcher(SQLExceptionHelper exceptionHelper, int jdbcBatchSize) { - super( exceptionHelper ); + public static class TestingBatch extends NonBatchingBatch { + public TestingBatch(Object key, SQLStatementLogger statementLogger, SQLExceptionHelper exceptionHelper, int jdbcBatchSize) { + super( key, statementLogger, exceptionHelper ); } } diff --git a/hibernate-core/src/test/resources/log4j.properties b/hibernate-core/src/test/resources/log4j.properties index a55e3d85fb..cd9c5224cf 100644 --- a/hibernate-core/src/test/resources/log4j.properties +++ b/hibernate-core/src/test/resources/log4j.properties @@ -8,10 +8,12 @@ log4j.rootLogger=info, stdout log4j.logger.org.hibernate.test=info log4j.logger.org.hibernate.tool.hbm2ddl=debug -log4j.logger.org.hibernate.engine.jdbc=trace +log4j.logger.org.hibernate.engine.jdbc.internal=trace +log4j.logger.org.hibernate.engine.jdbc.internal.proxy=trace +log4j.logger.org.hibernate.engine.jdbc.batch.internal=trace log4j.logger.org.hibernate.hql.ast.QueryTranslatorImpl=trace log4j.logger.org.hibernate.hql.ast.HqlSqlWalker=trace log4j.logger.org.hibernate.hql.ast.SqlGenerator=trace log4j.logger.org.hibernate.hql.ast.AST=trace log4j.logger.org.hibernate.type.descriptor.sql.BasicBinder=trace -log4j.logger.org.hibernate.type.BasicTypeRegistry=trace \ No newline at end of file +log4j.logger.org.hibernate.type.BasicTypeRegistry=trace