From d80c6ac0f978509f6d7ce9120377162a4910ec45 Mon Sep 17 00:00:00 2001 From: Vlad Mihalcea Date: Thu, 30 Mar 2017 17:21:23 +0300 Subject: [PATCH] HHH-11600 - Sap HANA PreparedStatement implements CallableStatement and is treated as such by Hibernate --- .../jdbc/internal/ResultSetReturnImpl.java | 30 ------ .../java/org/hibernate/loader/Loader.java | 97 ++++++++++++++++--- 2 files changed, 85 insertions(+), 42 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/ResultSetReturnImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/ResultSetReturnImpl.java index 3f753f801f..47f3251ad9 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/ResultSetReturnImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/ResultSetReturnImpl.java @@ -31,8 +31,6 @@ public class ResultSetReturnImpl implements ResultSetReturn { private final SqlStatementLogger sqlStatementLogger; private final SqlExceptionHelper sqlExceptionHelper; - private boolean isJdbc4 = true; - /** * Constructs a ResultSetReturnImpl * @@ -56,15 +54,6 @@ public class ResultSetReturnImpl implements ResultSetReturn { public ResultSet extract(PreparedStatement statement) { // IMPL NOTE : SQL logged by caller try { - if ( isTypeOf( statement, CallableStatement.class ) ) { - // We actually need to extract from Callable statement. Although - // this seems needless, Oracle can return an - // OracleCallableStatementWrapper that finds its way to this method, - // rather than extract(CallableStatement). See HHH-8022. - final CallableStatement callableStatement = statement.unwrap( CallableStatement.class ); - return extract( callableStatement ); - } - final ResultSet rs; try { jdbcExecuteStatementStart(); @@ -89,25 +78,6 @@ public class ResultSetReturnImpl implements ResultSetReturn { jdbcCoordinator.getJdbcSessionOwner().getJdbcSessionContext().getObserver().jdbcExecuteStatementStart(); } - private boolean isTypeOf(final Statement statement, final Class type) { - if ( isJdbc4 ) { - try { - // This is "more correct" than #isInstance, but not always supported. - return statement.isWrapperFor( type ); - } - catch (SQLException e) { - // No operation - } - catch (Throwable e) { - // No operation. Note that this catches more than just SQLException to - // cover edge cases where a driver might throw an UnsupportedOperationException, AbstractMethodError, - // etc. If so, skip permanently. - isJdbc4 = false; - } - } - return type.isInstance( statement ); - } - @Override public ResultSet extract(CallableStatement callableStatement) { // IMPL NOTE : SQL logged by caller diff --git a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java index 0f9ce142f8..ee63ac93af 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java @@ -108,6 +108,8 @@ public abstract class Loader { private final boolean referenceCachingEnabled; + private boolean isJdbc4 = true; + public Loader(SessionFactoryImplementor factory) { this.factory = factory; this.referenceCachingEnabled = factory.getSessionFactoryOptions().isDirectReferenceCacheEntriesEnabled(); @@ -1907,15 +1909,54 @@ public abstract class Loader { sql = preprocessSQL( sql, queryParameters, getFactory().getDialect(), afterLoadActions ); final PreparedStatement st = prepareQueryStatement( sql, queryParameters, limitHandler, scroll, session ); - return new SqlStatementWrapper( - st, getResultSet( + + final ResultSet rs; + + if( queryParameters.isCallable() && isTypeOf( st, CallableStatement.class ) ) { + final CallableStatement cs = st.unwrap( CallableStatement.class ); + + rs = getResultSet( + cs, + queryParameters.getRowSelection(), + limitHandler, + queryParameters.hasAutoDiscoverScalarTypes(), + session + ); + } + else { + rs = getResultSet( st, queryParameters.getRowSelection(), limitHandler, queryParameters.hasAutoDiscoverScalarTypes(), session - ) + ); + } + + return new SqlStatementWrapper( + st, + rs ); + + } + + private boolean isTypeOf(final Statement statement, final Class type) { + if ( isJdbc4 ) { + try { + // This is "more correct" than #isInstance, but not always supported. + return statement.isWrapperFor( type ); + } + catch (SQLException e) { + // No operation + } + catch (Throwable e) { + // No operation. Note that this catches more than just SQLException to + // cover edge cases where a driver might throw an UnsupportedOperationException, AbstractMethodError, + // etc. If so, skip permanently. + isJdbc4 = false; + } + } + return type.isInstance( statement ); } /** @@ -2121,16 +2162,8 @@ public abstract class Loader { final SharedSessionContractImplementor session) throws SQLException, HibernateException { try { ResultSet rs = session.getJdbcCoordinator().getResultSetReturn().extract( st ); - rs = wrapResultSetIfEnabled( rs, session ); - if ( !limitHandler.supportsLimitOffset() || !LimitHelper.useLimit( limitHandler, selection ) ) { - advance( rs, selection ); - } - - if ( autodiscovertypes ) { - autoDiscoverTypes( rs ); - } - return rs; + return processResultSet(rs, selection, limitHandler, autodiscovertypes, session); } catch (SQLException | HibernateException e) { session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( st ); @@ -2139,6 +2172,46 @@ public abstract class Loader { } } + /** + * Execute given CallableStatement, advance to the first result and return SQL ResultSet. + */ + protected final ResultSet getResultSet( + final CallableStatement st, + final RowSelection selection, + final LimitHandler limitHandler, + final boolean autodiscovertypes, + final SharedSessionContractImplementor session) throws SQLException, HibernateException { + try { + ResultSet rs = session.getJdbcCoordinator().getResultSetReturn().extract( st ); + + return processResultSet(rs, selection, limitHandler, autodiscovertypes, session); + } + catch (SQLException | HibernateException e) { + session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( st ); + session.getJdbcCoordinator().afterStatementExecution(); + throw e; + } + } + + private ResultSet processResultSet( + ResultSet rs, + final RowSelection selection, + final LimitHandler limitHandler, + final boolean autodiscovertypes, + final SharedSessionContractImplementor session + ) throws SQLException, HibernateException { + rs = wrapResultSetIfEnabled( rs, session ); + + if ( !limitHandler.supportsLimitOffset() || !LimitHelper.useLimit( limitHandler, selection ) ) { + advance( rs, selection ); + } + + if ( autodiscovertypes ) { + autoDiscoverTypes( rs ); + } + return rs; + } + protected void autoDiscoverTypes(ResultSet rs) { throw new AssertionFailure( "Auto discover types not supported in this loader" );