diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/DefaultSchemaNameResolver.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/DefaultSchemaNameResolver.java index af5ed2bf0e..2ad44fc217 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/DefaultSchemaNameResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/DefaultSchemaNameResolver.java @@ -28,12 +28,14 @@ public class DefaultSchemaNameResolver implements SchemaNameResolver { public static final DefaultSchemaNameResolver INSTANCE = new DefaultSchemaNameResolver(); - private SchemaNameResolver delegate; + // NOTE: The actual delegate should not be cached in DefaultSchemaNameResolver because, + // in the case of multiple data sources, there may be a data source that + // requires a different delegate. See HHH-12392. private DefaultSchemaNameResolver() { } - private void determineAppropriateResolverDelegate(Connection connection) { + private SchemaNameResolver determineAppropriateResolverDelegate(Connection connection) { // unfortunately Connection#getSchema is only available in Java 1.7 and above // and Hibernate still baselines on 1.6. So for now, use reflection and // leverage the Connection#getSchema method if it is available. @@ -45,29 +47,32 @@ private void determineAppropriateResolverDelegate(Connection connection) { // If the JDBC driver does not implement the Java 7 spec, but the JRE is Java 7 // then the getSchemaMethod is not null but the call to getSchema() throws an java.lang.AbstractMethodError connection.getSchema(); - delegate = new SchemaNameResolverJava17Delegate(); + return new SchemaNameResolverJava17Delegate(); } catch (java.lang.AbstractMethodError e) { log.debugf( "Unable to use Java 1.7 Connection#getSchema" ); - delegate = SchemaNameResolverFallbackDelegate.INSTANCE; + return SchemaNameResolverFallbackDelegate.INSTANCE; } } else { log.debugf( "Unable to use Java 1.7 Connection#getSchema" ); - delegate = SchemaNameResolverFallbackDelegate.INSTANCE; + return SchemaNameResolverFallbackDelegate.INSTANCE; } } catch (Exception ignore) { log.debugf( "Unable to use Java 1.7 Connection#getSchema : An error occurred trying to resolve the connection default schema resolver: " + ignore.getMessage() ); - delegate = SchemaNameResolverFallbackDelegate.INSTANCE; + return SchemaNameResolverFallbackDelegate.INSTANCE; } } @Override public String resolveSchemaName(Connection connection, Dialect dialect) throws SQLException { - determineAppropriateResolverDelegate( connection ); + // NOTE: delegate should not be cached in DefaultSchemaNameResolver because, + // in the case of multiple data sources, there may be a data source that + // requires a different delegate. See HHH-12392. + final SchemaNameResolver delegate = determineAppropriateResolverDelegate( connection ); return delegate.resolveSchemaName( connection, dialect ); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/jdbc/env/DefaultSchemaNameResolverTest.java b/hibernate-core/src/test/java/org/hibernate/test/jdbc/env/DefaultSchemaNameResolverTest.java new file mode 100644 index 0000000000..50ac4ab7af --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/jdbc/env/DefaultSchemaNameResolverTest.java @@ -0,0 +1,155 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.jdbc.env; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.env.internal.DefaultSchemaNameResolver; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Gail Badner + */ +public class DefaultSchemaNameResolverTest { + private static final String SCHEMA_NAME = "theSchemaName"; + private static final String GET_CURRENT_SCHEMA_NAME_COMMAND = "get the schema name"; + + @Test + public void testSecondConnectionDoesNotSupportGetSchemaName() throws SQLException { + final Connection connectionSupportsGetSchemaName = + ConnectionProxy.generateProxy( new ConnectionProxy( SCHEMA_NAME ) ); + String schemaName = DefaultSchemaNameResolver.INSTANCE.resolveSchemaName( + connectionSupportsGetSchemaName, + new Dialect() { + } + ); + assertEquals( SCHEMA_NAME, schemaName ); + + final Connection connectionNotSupportGetSchemaName = + ConnectionProxy.generateProxy( new ConnectionProxy( null ) ); + schemaName = DefaultSchemaNameResolver.INSTANCE.resolveSchemaName( + connectionNotSupportGetSchemaName, + new Dialect() { + @Override + public String getCurrentSchemaCommand() { + return GET_CURRENT_SCHEMA_NAME_COMMAND ; + } + } + ); + assertEquals( SCHEMA_NAME, schemaName ); + } + + public static class ConnectionProxy implements InvocationHandler { + private String schemaName; + + ConnectionProxy(String schemaName) { + this.schemaName = schemaName; + } + + public static Connection generateProxy(ConnectionProxy handler) { + return (Connection) Proxy.newProxyInstance( + getProxyClassLoader(), + new Class[] { Connection.class }, + handler + ); + } + + private static ClassLoader getProxyClassLoader() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if ( cl == null ) { + cl = Connection.class.getClassLoader(); + } + return cl; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ( method.getName().equals( "getSchema" ) && args == null ) { + if ( schemaName != null ) { + return schemaName; + } + throw new AbstractMethodError( "getSchema is not implemented" ); + } + else if ( method.getName().equals( "createStatement" ) && args == null ) { + return StatementProxy.generateProxy( new StatementProxy() ); + } + throw new UnsupportedOperationException( "Unexpected call ResultSet." + method.getName() ); + } + } + + public static class StatementProxy implements InvocationHandler { + + public static Statement generateProxy(StatementProxy handler) { + return (Statement) Proxy.newProxyInstance( + getProxyClassLoader(), + new Class[] { Statement.class }, + handler + ); + } + + private static ClassLoader getProxyClassLoader() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if ( cl == null ) { + cl = Statement.class.getClassLoader(); + } + return cl; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ( method.getName().equals( "executeQuery" ) && args.length == 1 && GET_CURRENT_SCHEMA_NAME_COMMAND.equals( args[0] ) ) { + return ResultSetProxy.generateProxy( new ResultSetProxy() ); + } + if ( method.getName().equals( "close" ) && args == null ) { + // nothing to do + return null; + } + throw new UnsupportedOperationException( "Unexpected call Statement." + method.getName() ); + } + } + + public static class ResultSetProxy implements InvocationHandler { + + public static ResultSet generateProxy(ResultSetProxy handler) { + return (ResultSet) Proxy.newProxyInstance( + getProxyClassLoader(), + new Class[] { ResultSet.class }, + handler + ); + } + + private static ClassLoader getProxyClassLoader() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if ( cl == null ) { + cl = ResultSet.class.getClassLoader(); + } + return cl; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ( method.getName().equals( "next" ) && args == null ) { + return true; + } + if ( method.getName().equals( "getString" ) && args.length == 1 && Integer.valueOf( 1 ).equals( args[0] )) { + return SCHEMA_NAME; + } + if ( method.getName().equals( "close" ) && args == null ) { + // nothing to do + return null; + } + throw new UnsupportedOperationException( "Unexpected call ResultSet." + method.getName() ); + } + } +}