From 2366fc89ab6e67ab06630b830bc40ea771216d3f Mon Sep 17 00:00:00 2001 From: Vlad Mihalcea Date: Thu, 22 Mar 2018 16:12:45 +0200 Subject: [PATCH 1/4] HHH-12423 - SecondaryTable is not taking into account the schema while mapping the entity (cherry picked from commit 511a5a3618114bfaac47af6b2cf1404e48af08bb) --- .../InFlightMetadataCollectorImpl.java | 14 +- .../boot/spi/InFlightMetadataCollector.java | 3 +- .../cfg/annotations/EntityBinder.java | 27 ++-- .../SecondaryTableSchemaTest.java | 146 ++++++++++++++++++ 4 files changed, 178 insertions(+), 12 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/secondarytable/SecondaryTableSchemaTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java index a343e606beee..5a5fa2a4b2c9 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/InFlightMetadataCollectorImpl.java @@ -44,6 +44,7 @@ import org.hibernate.boot.model.relational.Database; import org.hibernate.boot.model.relational.ExportableProducer; import org.hibernate.boot.model.relational.Namespace; +import org.hibernate.boot.model.relational.QualifiedTableName; import org.hibernate.boot.model.source.internal.ImplicitColumnNamingSecondPass; import org.hibernate.boot.model.source.spi.LocalMetadataBuildingContext; import org.hibernate.boot.spi.AttributeConverterAutoApplyHandler; @@ -1366,8 +1367,17 @@ public void addSecondaryTable(LocalMetadataBuildingContext buildingContext, Iden } @Override - public void addSecondaryTable(Identifier logicalName, Join secondaryTableJoin) { - if ( Identifier.areEqual( primaryTableLogicalName, logicalName ) ) { + public void addSecondaryTable(QualifiedTableName logicalQualifiedTableName, Join secondaryTableJoin) { + Identifier logicalName = logicalQualifiedTableName.getTableName(); + if ( Identifier.areEqual( + Identifier.toIdentifier( + new QualifiedTableName( + Identifier.toIdentifier( primaryTable.getCatalog() ), + Identifier.toIdentifier( primaryTable.getSchema() ), + primaryTableLogicalName + ).render() + ), + Identifier.toIdentifier( logicalQualifiedTableName.render() ) ) ) { throw new DuplicateSecondaryTableException( logicalName ); } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/InFlightMetadataCollector.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/InFlightMetadataCollector.java index 1b9a89fe020c..9d4b532df69d 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/InFlightMetadataCollector.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/InFlightMetadataCollector.java @@ -23,6 +23,7 @@ import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject; import org.hibernate.boot.model.relational.Database; +import org.hibernate.boot.model.relational.QualifiedTableName; import org.hibernate.boot.model.source.spi.LocalMetadataBuildingContext; import org.hibernate.cfg.AnnotatedClassType; import org.hibernate.cfg.AttributeConverterDefinition; @@ -300,7 +301,7 @@ interface DelayedPropertyReferenceHandler extends Serializable { interface EntityTableXref { void addSecondaryTable(LocalMetadataBuildingContext buildingContext, Identifier logicalName, Join secondaryTableJoin); - void addSecondaryTable(Identifier logicalName, Join secondaryTableJoin); + void addSecondaryTable(QualifiedTableName logicalName, Join secondaryTableJoin); Table resolveTable(Identifier tableName); Table getPrimaryTable(); Join locateJoin(Identifier tableName); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java index 0c2fbc76b22f..8d5b593ae9fc 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java @@ -61,6 +61,7 @@ import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.ImplicitEntityNameSource; import org.hibernate.boot.model.naming.NamingStrategyHelper; +import org.hibernate.boot.model.relational.QualifiedTableName; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.boot.spi.InFlightMetadataCollector; import org.hibernate.boot.spi.MetadataBuildingContext; @@ -935,26 +936,34 @@ private Join addJoin( final Object joinColumns; final List uniqueConstraintHolders; - final Identifier logicalName; + final QualifiedTableName logicalName; if ( secondaryTable != null ) { schema = secondaryTable.schema(); catalog = secondaryTable.catalog(); - logicalName = context.getMetadataCollector() + logicalName = new QualifiedTableName( + Identifier.toIdentifier( catalog ), + Identifier.toIdentifier( schema ), + context.getMetadataCollector() .getDatabase() .getJdbcEnvironment() .getIdentifierHelper() - .toIdentifier( secondaryTable.name() ); + .toIdentifier( secondaryTable.name() ) + ); joinColumns = secondaryTable.pkJoinColumns(); uniqueConstraintHolders = TableBinder.buildUniqueConstraintHolders( secondaryTable.uniqueConstraints() ); } else if ( joinTable != null ) { schema = joinTable.schema(); catalog = joinTable.catalog(); - logicalName = context.getMetadataCollector() - .getDatabase() - .getJdbcEnvironment() - .getIdentifierHelper() - .toIdentifier( joinTable.name() ); + logicalName = new QualifiedTableName( + Identifier.toIdentifier( catalog ), + Identifier.toIdentifier( schema ), + context.getMetadataCollector() + .getDatabase() + .getJdbcEnvironment() + .getIdentifierHelper() + .toIdentifier( joinTable.name() ) + ); joinColumns = joinTable.joinColumns(); uniqueConstraintHolders = TableBinder.buildUniqueConstraintHolders( joinTable.uniqueConstraints() ); } @@ -965,7 +974,7 @@ else if ( joinTable != null ) { final Table table = TableBinder.buildAndFillTable( schema, catalog, - logicalName, + logicalName.getTableName(), false, uniqueConstraintHolders, null, diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/secondarytable/SecondaryTableSchemaTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/secondarytable/SecondaryTableSchemaTest.java new file mode 100644 index 000000000000..28de8472e6ef --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/secondarytable/SecondaryTableSchemaTest.java @@ -0,0 +1,146 @@ +/* + * 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.annotations.secondarytable; + +import java.io.Serializable; +import java.net.URL; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.ForeignKey; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.PrimaryKeyJoinColumn; +import javax.persistence.SecondaryTable; +import javax.persistence.Table; + +import org.hibernate.Criteria; +import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.annotations.OptimisticLockType; +import org.hibernate.annotations.OptimisticLocking; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Environment; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.hibernate.testing.RequiresDialect; +import org.hibernate.testing.transaction.TransactionUtil; +import org.hibernate.test.schemaupdate.foreignkeys.definition.AbstractForeignKeyDefinitionTest; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertTrue; + +/** + * @author Vlad Mihalcea + */ +@RequiresDialect(value = H2Dialect.class) +public class SecondaryTableSchemaTest + extends BaseEntityManagerFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Cluster.class, + }; + } + + protected void addConfigOptions(Map options) { + options.put( + AvailableSettings.URL, + options.get( AvailableSettings.URL ) + ";INIT=CREATE SCHEMA IF NOT EXISTS schema1\\;CREATE SCHEMA IF NOT EXISTS schema2;" + ); + } + + @Test + public void test() { + doInJPA( this::entityManagerFactory, entityManager -> { + List clusters = entityManager.createQuery( "select c from Cluster c" ).getResultList(); + + assertTrue(clusters.isEmpty()); + } ); + } + + @Entity(name = "Cluster") + @Table(name = "cluster", schema = "schema1") + @SecondaryTable(name = "Cluster", schema="schema2", pkJoinColumns = { @PrimaryKeyJoinColumn(name = "clusterid") }) + @org.hibernate.annotations.Table(appliesTo = "Cluster", optional = false) + @OptimisticLocking(type = OptimisticLockType.DIRTY) + @DynamicUpdate + public static class Cluster implements Serializable { + private static final long serialVersionUID = 3965099001305947412L; + + @Id + @Column(name = "objid") + private Long id; + + private String uuid; + + private String resourceKey; + + private String name; + + @Column(name = "lastSync", table = "Cluster") + private Long lastSync; + + @Column(name = "healthStatus", table = "Cluster") + private Integer healthStatus; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getResourceKey() { + return resourceKey; + } + + public void setResourceKey(String resourceKey) { + this.resourceKey = resourceKey; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public Long getLastSync() { + return lastSync; + } + + public void setLastSync(Long lastSync) { + this.lastSync = lastSync; + } + + public Integer getHealthStatus() { + return healthStatus; + } + + public void setHealthStatus(Integer healthStatus) { + this.healthStatus = healthStatus; + } + + } +} From 432420beb2d4596a74267ef1d6049ee057e4323c Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Thu, 10 May 2018 16:49:21 -0700 Subject: [PATCH 2/4] HHH-12423 : Fix test to work on pre-5.2 --- .../SecondaryTableSchemaTest.java | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/secondarytable/SecondaryTableSchemaTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/secondarytable/SecondaryTableSchemaTest.java index 28de8472e6ef..588d693a9358 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/secondarytable/SecondaryTableSchemaTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/secondarytable/SecondaryTableSchemaTest.java @@ -7,36 +7,26 @@ package org.hibernate.test.annotations.secondarytable; import java.io.Serializable; -import java.net.URL; -import java.util.Iterator; import java.util.List; -import java.util.Map; import javax.persistence.Column; import javax.persistence.Entity; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.SecondaryTable; import javax.persistence.Table; -import org.hibernate.Criteria; +import org.hibernate.Session; import org.hibernate.annotations.DynamicUpdate; import org.hibernate.annotations.OptimisticLockType; import org.hibernate.annotations.OptimisticLocking; import org.hibernate.cfg.AvailableSettings; -import org.hibernate.cfg.Environment; -import org.hibernate.dialect.Dialect; +import org.hibernate.cfg.Configuration; import org.hibernate.dialect.H2Dialect; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.testing.RequiresDialect; -import org.hibernate.testing.transaction.TransactionUtil; -import org.hibernate.test.schemaupdate.foreignkeys.definition.AbstractForeignKeyDefinitionTest; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; -import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; import static org.junit.Assert.assertTrue; /** @@ -44,7 +34,7 @@ */ @RequiresDialect(value = H2Dialect.class) public class SecondaryTableSchemaTest - extends BaseEntityManagerFunctionalTestCase { + extends BaseCoreFunctionalTestCase { @Override protected Class[] getAnnotatedClasses() { @@ -53,20 +43,20 @@ protected Class[] getAnnotatedClasses() { }; } - protected void addConfigOptions(Map options) { - options.put( + @Override + protected void configure(Configuration configuration) { + configuration.setProperty( AvailableSettings.URL, - options.get( AvailableSettings.URL ) + ";INIT=CREATE SCHEMA IF NOT EXISTS schema1\\;CREATE SCHEMA IF NOT EXISTS schema2;" + configuration.getProperty( AvailableSettings.URL ) + ";INIT=CREATE SCHEMA IF NOT EXISTS schema1\\;CREATE SCHEMA IF NOT EXISTS schema2;" ); } @Test public void test() { - doInJPA( this::entityManagerFactory, entityManager -> { - List clusters = entityManager.createQuery( "select c from Cluster c" ).getResultList(); - - assertTrue(clusters.isEmpty()); - } ); + Session session = openSession(); + session.getTransaction().begin(); + List clusters = session.createQuery( "select c from Cluster c" ).list(); + assertTrue(clusters.isEmpty()); } @Entity(name = "Cluster") From 26b5dca21f544a59e2aaea3948494091ffb4bbf8 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Thu, 10 May 2018 17:04:12 -0700 Subject: [PATCH 3/4] HHH-12392 : Caching SchemaResolver delegate with multiple data sources --- .../internal/DefaultSchemaNameResolver.java | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) 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 64fdc5f86758..05e6a7066462 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 @@ -34,38 +34,36 @@ private DefaultSchemaNameResolver() { } private void determineAppropriateResolverDelegate(Connection connection) { - if ( delegate == null ) { - // 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. - try { - final Class jdbcConnectionClass = connection.getClass(); - final Method getSchemaMethod = jdbcConnectionClass.getMethod( "getSchema" ); - if ( getSchemaMethod != null && getSchemaMethod.getReturnType().equals( String.class ) ) { - try { - // 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 - delegate = new SchemaNameResolverJava17Delegate( getSchemaMethod ); - // Connection#getSchema was introduced into jdk7. - // Since 5.1 is supposed to have jdk6 source, we can't call Connection#getSchema directly. - // Make sure it's possible to resolve the schema without taking dialect into account. - delegate.resolveSchemaName( connection, null ); - } - catch (java.lang.AbstractMethodError e) { - log.debugf( "Unable to use Java 1.7 Connection#getSchema" ); - delegate = SchemaNameResolverFallbackDelegate.INSTANCE; - } + // 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. + try { + final Class jdbcConnectionClass = connection.getClass(); + final Method getSchemaMethod = jdbcConnectionClass.getMethod( "getSchema" ); + if ( getSchemaMethod != null && getSchemaMethod.getReturnType().equals( String.class ) ) { + try { + // 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 + delegate = new SchemaNameResolverJava17Delegate( getSchemaMethod ); + // Connection#getSchema was introduced into jdk7. + // Since 5.1 is supposed to have jdk6 source, we can't call Connection#getSchema directly. + // Make sure it's possible to resolve the schema without taking dialect into account. + delegate.resolveSchemaName( connection, null ); } - else { + catch (java.lang.AbstractMethodError e) { log.debugf( "Unable to use Java 1.7 Connection#getSchema" ); delegate = SchemaNameResolverFallbackDelegate.INSTANCE; } } - catch (Exception ignore) { + else { + log.debugf( "Unable to use Java 1.7 Connection#getSchema" ); delegate = SchemaNameResolverFallbackDelegate.INSTANCE; - log.debugf( "Unable to resolve connection default schema : " + ignore.getMessage() ); } } + catch (Exception ignore) { + delegate = SchemaNameResolverFallbackDelegate.INSTANCE; + log.debugf( "Unable to resolve connection default schema : " + ignore.getMessage() ); + } } @Override From 0de7fd9e9b68df2113c75c737129ff13ee88c244 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Thu, 10 May 2018 17:13:10 -0700 Subject: [PATCH 4/4] HHH-12562 : Remove DefaultSchemaNameResolver#delegate since the value should not be cached --- .../internal/DefaultSchemaNameResolver.java | 21 ++- .../env/DefaultSchemaNameResolverTest.java | 155 ++++++++++++++++++ 2 files changed, 168 insertions(+), 8 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/jdbc/env/DefaultSchemaNameResolverTest.java 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 05e6a7066462..58e8fd65ed0b 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,13 @@ 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. @@ -44,31 +45,35 @@ private void determineAppropriateResolverDelegate(Connection connection) { try { // 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 - delegate = new SchemaNameResolverJava17Delegate( getSchemaMethod ); + final SchemaNameResolver delegate = new SchemaNameResolverJava17Delegate( getSchemaMethod ); // Connection#getSchema was introduced into jdk7. // Since 5.1 is supposed to have jdk6 source, we can't call Connection#getSchema directly. // Make sure it's possible to resolve the schema without taking dialect into account. delegate.resolveSchemaName( connection, null ); + return delegate; } 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) { - delegate = SchemaNameResolverFallbackDelegate.INSTANCE; log.debugf( "Unable to resolve connection default schema : " + ignore.getMessage() ); + 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 000000000000..e2c96ce8d839 --- /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() ); + } + } +} \ No newline at end of file