HHH-7984 - Oracle callable statement closing

Conflicts:
	hibernate-core/src/main/java/org/hibernate/loader/Loader.java
	hibernate-core/src/main/java/org/hibernate/loader/entity/DynamicBatchingEntityLoaderBuilder.java
This commit is contained in:
Lukasz Antoniak 2013-03-25 13:48:55 -04:00 committed by Brett Meyer
parent 2fb5a9292d
commit 62cea68007
10 changed files with 92 additions and 39 deletions

View File

@ -370,15 +370,16 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator {
}
@Override
public void register(ResultSet resultSet) {
public void register(ResultSet resultSet, Statement statement) {
LOG.tracev( "Registering result set [{0}]", resultSet );
Statement statement;
if ( statement == null ) {
try {
statement = resultSet.getStatement();
statement = resultSet.getStatement(); // best guess
}
catch ( SQLException e ) {
throw exceptionHelper.convert( e, "unable to access statement from resultset" );
}
}
if ( statement != null ) {
if ( LOG.isEnabled( Level.WARN ) && !xref.containsKey( statement ) ) {
LOG.unregisteredStatement();

View File

@ -54,7 +54,7 @@ public class ResultSetReturnImpl implements ResultSetReturn {
}
try {
ResultSet rs = statement.executeQuery();
postExtract( rs );
postExtract( rs, statement );
return rs;
}
catch ( SQLException e ) {
@ -68,7 +68,7 @@ public class ResultSetReturnImpl implements ResultSetReturn {
// sql logged by StatementPreparerImpl
ResultSet rs = jdbcCoordinator.getLogicalConnection().getJdbcServices()
.getDialect().getResultSet( statement );
postExtract( rs );
postExtract( rs, statement );
return rs;
}
catch ( SQLException e ) {
@ -82,7 +82,7 @@ public class ResultSetReturnImpl implements ResultSetReturn {
.getSqlStatementLogger().logStatement( sql );
try {
ResultSet rs = statement.executeQuery( sql );
postExtract( rs );
postExtract( rs, statement );
return rs;
}
catch ( SQLException e ) {
@ -100,7 +100,7 @@ public class ResultSetReturnImpl implements ResultSetReturn {
}
}
ResultSet rs = statement.getResultSet();
postExtract( rs );
postExtract( rs, statement );
return rs;
}
catch ( SQLException e ) {
@ -119,7 +119,7 @@ public class ResultSetReturnImpl implements ResultSetReturn {
}
}
ResultSet rs = statement.getResultSet();
postExtract( rs );
postExtract( rs, statement );
return rs;
}
catch ( SQLException e ) {
@ -157,8 +157,8 @@ public class ResultSetReturnImpl implements ResultSetReturn {
.getSqlExceptionHelper();
}
private void postExtract(ResultSet rs) {
if ( rs != null ) jdbcCoordinator.register( rs );
private void postExtract(ResultSet rs, Statement st) {
if ( rs != null ) jdbcCoordinator.register( rs, st );
}
}

View File

@ -170,10 +170,15 @@ public interface JdbcCoordinator extends Serializable {
/**
* Register a JDBC result set.
* <p/>
* Implementation note: Second parameter has been introduced to prevent
* multiple registrations of the same statement in case {@link ResultSet#getStatement()}
* does not return original {@link Statement} object.
*
* @param resultSet The result set to register.
* @param statement Statement from which {@link ResultSet} has been generated.
*/
public void register(ResultSet resultSet);
public void register(ResultSet resultSet, Statement statement);
/**
* Release a previously registered result set.

View File

@ -954,8 +954,9 @@ public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator
try {
final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>();
final ResultSet rs = executeQueryStatement( queryParameters, false, afterLoadActions, session );
final PreparedStatement st = (PreparedStatement) rs.getStatement();
final SqlStatementWrapper wrapper = executeQueryStatement( queryParameters, false, afterLoadActions, session );
final ResultSet rs = wrapper.getResultSet();
final PreparedStatement st = (PreparedStatement) wrapper.getStatement();
HolderInstantiator hi = HolderInstantiator.createClassicHolderInstantiator(holderConstructor, queryParameters.getResultTransformer());
Iterator result = new IteratorImpl( rs, st, session, queryParameters.isReadOnly( session ), returnTypes, getColumnNames(), hi );

View File

@ -896,9 +896,9 @@ public abstract class Loader {
final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>();
final ResultSet rs = executeQueryStatement( queryParameters, false, afterLoadActions, session );
final Statement st = rs.getStatement();
final int entitySpan = getEntityPersisters().length;
final SqlStatementWrapper wrapper = executeQueryStatement( queryParameters, false, afterLoadActions, session );
final ResultSet rs = wrapper.getResultSet();
final Statement st = wrapper.getStatement();
// would be great to move all this below here into another method that could also be used
// from the new scrolling stuff.
@ -1803,7 +1803,7 @@ public abstract class Loader {
* Process query string by applying filters, LIMIT clause, locks and comments if necessary.
* Finally execute SQL statement and advance to the first row.
*/
protected ResultSet executeQueryStatement(
protected SqlStatementWrapper executeQueryStatement(
final QueryParameters queryParameters,
final boolean scroll,
List<AfterLoadAction> afterLoadActions,
@ -1811,10 +1811,10 @@ public abstract class Loader {
return executeQueryStatement( getSQLString(), queryParameters, scroll, afterLoadActions, session );
}
protected ResultSet executeQueryStatement(
final String sqlStatement,
final QueryParameters queryParameters,
final boolean scroll,
protected SqlStatementWrapper executeQueryStatement(
String sqlStatement,
QueryParameters queryParameters,
boolean scroll,
List<AfterLoadAction> afterLoadActions,
final SessionImplementor session) throws SQLException {
@ -1829,7 +1829,7 @@ public abstract class Loader {
sql = preprocessSQL( sql, queryParameters, getFactory().getDialect(), afterLoadActions );
final PreparedStatement st = prepareQueryStatement( sql, queryParameters, limitHandler, scroll, session );
return getResultSet( st, queryParameters.getRowSelection(), limitHandler, queryParameters.hasAutoDiscoverScalarTypes(), session );
return new SqlStatementWrapper( st, getResultSet( st, queryParameters.getRowSelection(), limitHandler, queryParameters.hasAutoDiscoverScalarTypes(), session ) );
}
/**
@ -2583,8 +2583,9 @@ public abstract class Loader {
if ( stats ) startTime = System.currentTimeMillis();
try {
final ResultSet rs = executeQueryStatement( queryParameters, true, Collections.<AfterLoadAction>emptyList(), session );
final PreparedStatement st = (PreparedStatement) rs.getStatement();
final SqlStatementWrapper wrapper = executeQueryStatement( queryParameters, true, Collections.<AfterLoadAction>emptyList(), session );
final ResultSet rs = wrapper.getResultSet();
final PreparedStatement st = (PreparedStatement) wrapper.getStatement();
if ( stats ) {
getFactory().getStatisticsImplementor().queryExecuted(
@ -2657,4 +2658,25 @@ public abstract class Loader {
public String toString() {
return getClass().getName() + '(' + getSQLString() + ')';
}
/**
* Wrapper class for {@link Statement} and associated {@link ResultSet}.
*/
protected static class SqlStatementWrapper {
private final Statement statement;
private final ResultSet resultSet;
private SqlStatementWrapper(Statement statement, ResultSet resultSet) {
this.resultSet = resultSet;
this.statement = statement;
}
public ResultSet getResultSet() {
return resultSet;
}
public Statement getStatement() {
return statement;
}
}
}

View File

@ -253,8 +253,9 @@ public class DynamicBatchingCollectionInitializerBuilder extends BatchingCollect
Integer.MAX_VALUE;
final List<AfterLoadAction> afterLoadActions = Collections.emptyList();
final ResultSet rs = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
final Statement st = rs.getStatement();
final SqlStatementWrapper wrapper = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
final ResultSet rs = wrapper.getResultSet();
final Statement st = wrapper.getStatement();
try {
processResultSet( rs, queryParameters, session, true, null, maxRows, afterLoadActions );
}

View File

@ -27,11 +27,9 @@ import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
import org.jboss.logging.Logger;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.dialect.pagination.LimitHelper;
@ -45,6 +43,7 @@ import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.pretty.MessageHelper;
import org.jboss.logging.Logger;
/**
* A BatchingEntityLoaderBuilder that builds UniqueEntityLoader instances capable of dynamically building
@ -254,9 +253,10 @@ public class DynamicBatchingEntityLoaderBuilder extends BatchingEntityLoaderBuil
selection.getMaxRows() :
Integer.MAX_VALUE;
final List<AfterLoadAction> afterLoadActions = Collections.emptyList();
final ResultSet rs = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
final Statement st = rs.getStatement();
final List<AfterLoadAction> afterLoadActions = new ArrayList<AfterLoadAction>();
final SqlStatementWrapper wrapper = executeQueryStatement( sql, queryParameters, false, afterLoadActions, session );
final ResultSet rs = wrapper.getResultSet();
final Statement st = wrapper.getStatement();
try {
return processResultSet( rs, queryParameters, session, false, null, maxRows, afterLoadActions );
}

View File

@ -510,8 +510,9 @@ public class QueryLoader extends BasicLoader {
if ( queryParameters.isCallable() ) {
throw new QueryException("iterate() not supported for callable statements");
}
final ResultSet rs = executeQueryStatement( queryParameters, false, Collections.<AfterLoadAction>emptyList(), session );
final PreparedStatement st = (PreparedStatement) rs.getStatement();
final SqlStatementWrapper wrapper = executeQueryStatement( queryParameters, false, Collections.<AfterLoadAction>emptyList(), session );
final ResultSet rs = wrapper.getResultSet();
final PreparedStatement st = (PreparedStatement) wrapper.getStatement();
final Iterator result = new IteratorImpl(
rs,
st,

View File

@ -129,7 +129,7 @@ public class BasicOperationsTest extends BaseCoreFunctionalTestCase {
String columnNamePattern = generateFinalNamePattern( meta, columnName );
ResultSet columnInfo = meta.getColumns( null, null, tableNamePattern, columnNamePattern );
s.getTransactionCoordinator().getJdbcCoordinator().register(columnInfo);
s.getTransactionCoordinator().getJdbcCoordinator().register(columnInfo, columnInfo.getStatement());
assertTrue( columnInfo.next() );
int dataType = columnInfo.getInt( "DATA_TYPE" );
s.getTransactionCoordinator().getJdbcCoordinator().release( columnInfo );

View File

@ -89,6 +89,28 @@ public class CursorFromCallableTest extends BaseCoreFunctionalTestCase {
session.close();
}
@Test
@TestForIssue( jiraKey = "HHH-7984" )
public void testStatementClosing() {
Session session = openSession();
session.getTransaction().begin();
// Reading maximum number of opened cursors requires SYS privileges.
// Verify statement closing with JdbcCoordinator#hasRegisteredResources() instead.
// BigDecimal maxCursors = (BigDecimal) session.createSQLQuery( "SELECT value FROM v$parameter WHERE name = 'open_cursors'" ).uniqueResult();
// for ( int i = 0; i < maxCursors + 10; ++i ) { named_query_execution }
Assert.assertEquals(
Arrays.asList( new NumValue( 1, "Line 1" ), new NumValue( 2, "Line 2" ) ),
session.getNamedQuery( "NumValue.getSomeValues" ).list()
);
JdbcCoordinator jdbcCoordinator = ( (SessionImplementor) session ).getTransactionCoordinator().getJdbcCoordinator();
Assert.assertFalse(
"Prepared statement and result set should be released after query execution.",
jdbcCoordinator.hasRegisteredResources()
);
session.getTransaction().commit();
session.close();
}
private void executeStatement(final String sql) {
final Session session = openSession();
session.getTransaction().begin();