diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedName.java b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedName.java index 7246d86e11..accffef431 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedName.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedName.java @@ -14,6 +14,11 @@ import org.hibernate.boot.model.naming.Identifier; *
  • {@link java.sql.DatabaseMetaData#isCatalogAtStart}
  • *
  • {@link java.sql.DatabaseMetaData#getCatalogSeparator()}
  • * + *

    + * Also, be careful about the usage of {@link #render}. If the intention is get get the name + * as used in the database, the {@link org.hibernate.engine.jdbc.env.spi.JdbcEnvironment} -> + * {@link org.hibernate.engine.jdbc.env.spi.QualifiedObjectNameFormatter#format} should be + * used instead. * * @author Steve Ebersole */ @@ -22,5 +27,14 @@ public interface QualifiedName { Identifier getSchemaName(); Identifier getObjectName(); + /** + * Returns a String-form of the qualified name. + *

    + * Depending on intention, may not be appropriate. May want + * {@link org.hibernate.engine.jdbc.env.spi.QualifiedObjectNameFormatter#format} + * instead. See {@link org.hibernate.engine.jdbc.env.spi.JdbcEnvironment#getQualifiedObjectNameFormatter} + * + * @return The string form + */ String render(); } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java index 77da722fda..92bd8623ff 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java @@ -23,6 +23,7 @@ import org.hibernate.boot.model.relational.InitCommand; import org.hibernate.boot.model.relational.Namespace; import org.hibernate.boot.model.relational.QualifiedTableName; import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.env.spi.QualifiedObjectNameFormatter; import org.hibernate.engine.spi.Mapping; import org.hibernate.internal.util.StringHelper; import org.hibernate.tool.hbm2ddl.ColumnMetadata; @@ -106,6 +107,11 @@ public class Table implements RelationalModel, Serializable, Exportable { this.isAbstract = isAbstract; } + /** + * @deprecated Should use {@link QualifiedObjectNameFormatter#format} on QualifiedObjectNameFormatter + * obtained from {@link org.hibernate.engine.jdbc.env.spi.JdbcEnvironment} + */ + @Deprecated public String getQualifiedName(Dialect dialect, String defaultCatalog, String defaultSchema) { if ( subselect != null ) { return "( " + subselect + " )"; @@ -120,6 +126,11 @@ public class Table implements RelationalModel, Serializable, Exportable { return qualify( usedCatalog, usedSchema, quotedName ); } + /** + * @deprecated Should use {@link QualifiedObjectNameFormatter#format} on QualifiedObjectNameFormatter + * obtained from {@link org.hibernate.engine.jdbc.env.spi.JdbcEnvironment} + */ + @Deprecated public static String qualify(String catalog, String schema, String table) { StringBuilder qualifiedName = new StringBuilder(); if ( catalog != null ) { 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 424154a1e1..61631bb958 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 @@ -22,6 +22,7 @@ import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.QueryException; import org.hibernate.TransientObjectException; +import org.hibernate.boot.model.relational.Database; import org.hibernate.cache.CacheException; import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy; import org.hibernate.cache.spi.entry.CacheEntryStructure; @@ -31,6 +32,7 @@ import org.hibernate.cache.spi.entry.UnstructuredCacheEntry; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.batch.internal.BasicBatchKey; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; @@ -230,9 +232,12 @@ public abstract class AbstractCollectionPersister CollectionRegionAccessStrategy cacheAccessStrategy, PersisterCreationContext creationContext) throws MappingException, CacheException { + final Database database = creationContext.getMetadata().getDatabase(); + final JdbcEnvironment jdbcEnvironment = database.getJdbcEnvironment(); + this.factory = creationContext.getSessionFactory(); this.cacheAccessStrategy = cacheAccessStrategy; - if ( factory.getSettings().isStructuredCacheEntriesEnabled() ) { + if ( factory.getSessionFactoryOptions().isStructuredCacheEntriesEnabled() ) { cacheEntryStructure = collectionBinding.isMap() ? StructuredMapCacheEntry.INSTANCE : StructuredCollectionCacheEntry.INSTANCE; @@ -260,11 +265,7 @@ public abstract class AbstractCollectionPersister isArray = collectionBinding.isArray(); subselectLoadable = collectionBinding.isSubselectLoadable(); - qualifiedTableName = table.getQualifiedName( - dialect, - factory.getSettings().getDefaultCatalogName(), - factory.getSettings().getDefaultSchemaName() - ); + qualifiedTableName = determineTableName( table, jdbcEnvironment ); int spacesSize = 1 + collectionBinding.getSynchronizedTables().size(); spaces = new String[spacesSize]; @@ -284,7 +285,7 @@ public abstract class AbstractCollectionPersister int batch = collectionBinding.getBatchSize(); if ( batch == -1 ) { - batch = factory.getSettings().getDefaultBatchFetchSize(); + batch = factory.getSessionFactoryOptions().getDefaultBatchFetchSize(); } batchSize = batch; @@ -585,6 +586,17 @@ public abstract class AbstractCollectionPersister initCollectionPropertyMap(); } + protected String determineTableName(Table table, JdbcEnvironment jdbcEnvironment) { + if ( table.getSubselect() != null ) { + return "( " + table.getSubselect() + " )"; + } + + return jdbcEnvironment.getQualifiedObjectNameFormatter().format( + table.getQualifiedTableName(), + jdbcEnvironment.getDialect() + ); + } + private class ColumnMapperImpl implements ColumnMapper { @Override public SqlValueReference[] map(String reference) { 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 05f969ce3a..ecfe0cff93 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 @@ -54,6 +54,7 @@ import org.hibernate.engine.internal.MutableEntityEntryFactory; import org.hibernate.engine.internal.StatefulPersistenceContext; import org.hibernate.engine.internal.Versioning; import org.hibernate.engine.jdbc.batch.internal.BasicBatchKey; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.spi.CachedNaturalIdValueSource; import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.CascadingActions; @@ -92,6 +93,7 @@ import org.hibernate.mapping.Component; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; import org.hibernate.mapping.Selectable; +import org.hibernate.mapping.Table; import org.hibernate.metadata.ClassMetadata; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.spi.PersisterCreationContext; @@ -5112,6 +5114,17 @@ public abstract class AbstractEntityPersister return 0; } + protected String determineTableName(Table table, JdbcEnvironment jdbcEnvironment) { + if ( table.getSubselect() != null ) { + return "( " + table.getSubselect() + " )"; + } + + return jdbcEnvironment.getQualifiedObjectNameFormatter().format( + table.getQualifiedTableName(), + jdbcEnvironment.getDialect() + ); + } + @Override public EntityEntryFactory getEntityEntryFactory() { return this.entityEntryFactory; diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java index 377586908b..4c8f96b770 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java @@ -18,9 +18,11 @@ import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.QueryException; +import org.hibernate.boot.model.relational.Database; import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy; import org.hibernate.engine.OptimisticLockStyle; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.DynamicFilterAliasGenerator; @@ -133,6 +135,8 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { super( persistentClass, cacheAccessStrategy, naturalIdRegionAccessStrategy, creationContext ); final SessionFactoryImplementor factory = creationContext.getSessionFactory(); + final Database database = creationContext.getMetadata().getDatabase(); + final JdbcEnvironment jdbcEnvironment = database.getJdbcEnvironment(); // DISCRIMINATOR @@ -201,28 +205,24 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { final int idColumnSpan = getIdentifierColumnSpan(); - ArrayList tables = new ArrayList(); - ArrayList keyColumns = new ArrayList(); - ArrayList keyColumnReaders = new ArrayList(); - ArrayList keyColumnReaderTemplates = new ArrayList(); - ArrayList cascadeDeletes = new ArrayList(); - Iterator titer = persistentClass.getTableClosureIterator(); - Iterator kiter = persistentClass.getKeyClosureIterator(); - while ( titer.hasNext() ) { - Table tab = (Table) titer.next(); - KeyValue key = (KeyValue) kiter.next(); - String tabname = tab.getQualifiedName( - factory.getDialect(), - factory.getSettings().getDefaultCatalogName(), - factory.getSettings().getDefaultSchemaName() - ); - tables.add( tabname ); + ArrayList tableNames = new ArrayList(); + ArrayList keyColumns = new ArrayList(); + ArrayList keyColumnReaders = new ArrayList(); + ArrayList keyColumnReaderTemplates = new ArrayList(); + ArrayList cascadeDeletes = new ArrayList(); + Iterator tItr = persistentClass.getTableClosureIterator(); + Iterator kItr = persistentClass.getKeyClosureIterator(); + while ( tItr.hasNext() ) { + final Table table = (Table) tItr.next(); + final KeyValue key = (KeyValue) kItr.next(); + final String tableName = determineTableName( table, jdbcEnvironment ); + tableNames.add( tableName ); String[] keyCols = new String[idColumnSpan]; String[] keyColReaders = new String[idColumnSpan]; String[] keyColReaderTemplates = new String[idColumnSpan]; - Iterator citer = key.getColumnIterator(); + Iterator cItr = key.getColumnIterator(); for ( int k = 0; k < idColumnSpan; k++ ) { - Column column = (Column) citer.next(); + Column column = (Column) cItr.next(); keyCols[k] = column.getQuotedName( factory.getDialect() ); keyColReaders[k] = column.getReadExpr( factory.getDialect() ); keyColReaderTemplates[k] = column.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() ); @@ -233,26 +233,21 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { cascadeDeletes.add( key.isCascadeDeleteEnabled() && factory.getDialect().supportsCascadeDelete() ); } - //Span of the tables directly mapped by this entity and super-classes, if any - coreTableSpan = tables.size(); + //Span of the tableNames directly mapped by this entity and super-classes, if any + coreTableSpan = tableNames.size(); isNullableTable = new boolean[persistentClass.getJoinClosureSpan()]; int tableIndex = 0; - Iterator joinIter = persistentClass.getJoinClosureIterator(); - while ( joinIter.hasNext() ) { - Join join = (Join) joinIter.next(); + Iterator joinItr = persistentClass.getJoinClosureIterator(); + while ( joinItr.hasNext() ) { + Join join = (Join) joinItr.next(); isNullableTable[tableIndex++] = join.isOptional(); Table table = join.getTable(); - - String tableName = table.getQualifiedName( - factory.getDialect(), - factory.getSettings().getDefaultCatalogName(), - factory.getSettings().getDefaultSchemaName() - ); - tables.add( tableName ); + final String tableName = determineTableName( table, jdbcEnvironment ); + tableNames.add( tableName ); KeyValue key = join.getKey(); int joinIdColumnSpan = key.getColumnSpan(); @@ -261,10 +256,10 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { String[] keyColReaders = new String[joinIdColumnSpan]; String[] keyColReaderTemplates = new String[joinIdColumnSpan]; - Iterator citer = key.getColumnIterator(); + Iterator cItr = key.getColumnIterator(); for ( int k = 0; k < joinIdColumnSpan; k++ ) { - Column column = (Column) citer.next(); + Column column = (Column) cItr.next(); keyCols[k] = column.getQuotedName( factory.getDialect() ); keyColReaders[k] = column.getReadExpr( factory.getDialect() ); keyColReaderTemplates[k] = column.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() ); @@ -275,64 +270,55 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { cascadeDeletes.add( key.isCascadeDeleteEnabled() && factory.getDialect().supportsCascadeDelete() ); } - naturalOrderTableNames = ArrayHelper.toStringArray( tables ); + naturalOrderTableNames = ArrayHelper.toStringArray( tableNames ); naturalOrderTableKeyColumns = ArrayHelper.to2DStringArray( keyColumns ); naturalOrderTableKeyColumnReaders = ArrayHelper.to2DStringArray( keyColumnReaders ); naturalOrderTableKeyColumnReaderTemplates = ArrayHelper.to2DStringArray( keyColumnReaderTemplates ); naturalOrderCascadeDeleteEnabled = ArrayHelper.toBooleanArray( cascadeDeletes ); - ArrayList subtables = new ArrayList(); - ArrayList isConcretes = new ArrayList(); - ArrayList isDeferreds = new ArrayList(); - ArrayList isLazies = new ArrayList(); + ArrayList subclassTableNames = new ArrayList(); + ArrayList isConcretes = new ArrayList(); + ArrayList isDeferreds = new ArrayList(); + ArrayList isLazies = new ArrayList(); - keyColumns = new ArrayList(); - titer = persistentClass.getSubclassTableClosureIterator(); - while ( titer.hasNext() ) { - Table tab = (Table) titer.next(); + keyColumns = new ArrayList(); + tItr = persistentClass.getSubclassTableClosureIterator(); + while ( tItr.hasNext() ) { + Table tab = (Table) tItr.next(); isConcretes.add( persistentClass.isClassOrSuperclassTable( tab ) ); isDeferreds.add( Boolean.FALSE ); isLazies.add( Boolean.FALSE ); - String tabname = tab.getQualifiedName( - factory.getDialect(), - factory.getSettings().getDefaultCatalogName(), - factory.getSettings().getDefaultSchemaName() - ); - subtables.add( tabname ); + final String tableName = determineTableName( tab, jdbcEnvironment ); + subclassTableNames.add( tableName ); String[] key = new String[idColumnSpan]; - Iterator citer = tab.getPrimaryKey().getColumnIterator(); + Iterator cItr = tab.getPrimaryKey().getColumnIterator(); for ( int k = 0; k < idColumnSpan; k++ ) { - key[k] = ( (Column) citer.next() ).getQuotedName( factory.getDialect() ); + key[k] = ( (Column) cItr.next() ).getQuotedName( factory.getDialect() ); } keyColumns.add( key ); } //Add joins - joinIter = persistentClass.getSubclassJoinClosureIterator(); - while ( joinIter.hasNext() ) { - Join join = (Join) joinIter.next(); + joinItr = persistentClass.getSubclassJoinClosureIterator(); + while ( joinItr.hasNext() ) { + final Join join = (Join) joinItr.next(); + final Table joinTable = join.getTable(); - Table tab = join.getTable(); - - isConcretes.add( persistentClass.isClassOrSuperclassTable( tab ) ); + isConcretes.add( persistentClass.isClassOrSuperclassTable( joinTable ) ); isDeferreds.add( join.isSequentialSelect() ); isLazies.add( join.isLazy() ); - String tabname = tab.getQualifiedName( - factory.getDialect(), - factory.getSettings().getDefaultCatalogName(), - factory.getSettings().getDefaultSchemaName() - ); - subtables.add( tabname ); + String joinTableName = determineTableName( joinTable, jdbcEnvironment ); + subclassTableNames.add( joinTableName ); String[] key = new String[idColumnSpan]; - Iterator citer = tab.getPrimaryKey().getColumnIterator(); + Iterator citer = joinTable.getPrimaryKey().getColumnIterator(); for ( int k = 0; k < idColumnSpan; k++ ) { key[k] = ( (Column) citer.next() ).getQuotedName( factory.getDialect() ); } keyColumns.add( key ); } - String[] naturalOrderSubclassTableNameClosure = ArrayHelper.toStringArray( subtables ); + String[] naturalOrderSubclassTableNameClosure = ArrayHelper.toStringArray( subclassTableNames ); String[][] naturalOrderSubclassTableKeyColumnClosure = ArrayHelper.to2DStringArray( keyColumns ); isClassOrSuperclassTable = ArrayHelper.toBooleanArray( isConcretes ); subclassTableSequentialSelect = ArrayHelper.toBooleanArray( isDeferreds ); @@ -347,9 +333,9 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { } /** - * Suppose an entity Client extends Person, mapped to the tables CLIENT and PERSON respectively. + * Suppose an entity Client extends Person, mapped to the tableNames CLIENT and PERSON respectively. * For the Client entity: - * naturalOrderTableNames -> PERSON, CLIENT; this reflects the sequence in which the tables are + * naturalOrderTableNames -> PERSON, CLIENT; this reflects the sequence in which the tableNames are * added to the meta-data when the annotated entities are processed. * However, in some instances, for example when generating joins, the CLIENT table needs to be * the first table as it will the driving table. @@ -357,7 +343,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { */ tableSpan = naturalOrderTableNames.length; - tableNames = reverse( naturalOrderTableNames, coreTableSpan ); + this.tableNames = reverse( naturalOrderTableNames, coreTableSpan ); tableKeyColumns = reverse( naturalOrderTableKeyColumns, coreTableSpan ); tableKeyColumnReaders = reverse( naturalOrderTableKeyColumnReaders, coreTableSpan ); tableKeyColumnReaderTemplates = reverse( naturalOrderTableKeyColumnReaderTemplates, coreTableSpan ); @@ -365,7 +351,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { subclassTableKeyColumnClosure = reverse( naturalOrderSubclassTableKeyColumnClosure, coreTableSpan ); spaces = ArrayHelper.join( - tableNames, + this.tableNames, ArrayHelper.toStringArray( persistentClass.getSynchronizedTables() ) ); @@ -408,10 +394,10 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { throw new AssertionFailure( "Tablespan does not match height of joined-subclass hiearchy." ); } - joinIter = persistentClass.getJoinClosureIterator(); + joinItr = persistentClass.getJoinClosureIterator(); int j = coreTableSpan; - while ( joinIter.hasNext() ) { - Join join = (Join) joinIter.next(); + while ( joinItr.hasNext() ) { + Join join = (Join) joinItr.next(); customSQLInsert[j] = join.getCustomSQLInsert(); insertCallable[j] = customSQLInsert[j] != null && join.isCustomInsertCallable(); @@ -444,7 +430,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { factory.getSettings().getDefaultCatalogName(), factory.getSettings().getDefaultSchemaName() ); - propertyTableNumbers[i] = getTableId( tabname, tableNames ); + propertyTableNumbers[i] = getTableId( tabname, this.tableNames ); naturalOrderPropertyTableNumbers[i] = getTableId( tabname, naturalOrderTableNames ); i++; } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java index 66d3341f25..85db93a138 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java @@ -17,8 +17,10 @@ import java.util.Set; import org.hibernate.HibernateException; import org.hibernate.MappingException; +import org.hibernate.boot.model.relational.Database; import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.DynamicFilterAliasGenerator; @@ -123,6 +125,9 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { final SessionFactoryImplementor factory = creationContext.getSessionFactory(); + final Database database = creationContext.getMetadata().getDatabase(); + final JdbcEnvironment jdbcEnvironment = database.getJdbcEnvironment(); + // CLASS + TABLE joinSpan = persistentClass.getJoinClosureSpan() + 1; @@ -131,11 +136,8 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { isNullableTable = new boolean[joinSpan]; keyColumnNames = new String[joinSpan][]; final Table table = persistentClass.getRootTable(); - qualifiedTableNames[0] = table.getQualifiedName( - factory.getDialect(), - factory.getSettings().getDefaultCatalogName(), - factory.getSettings().getDefaultSchemaName() - ); + qualifiedTableNames[0] = determineTableName( table, jdbcEnvironment ); + isInverseTable[0] = false; isNullableTable[0] = false; keyColumnNames[0] = getIdentifierColumnNames(); @@ -174,11 +176,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { int j = 1; while ( joinIter.hasNext() ) { Join join = (Join) joinIter.next(); - qualifiedTableNames[j] = join.getTable().getQualifiedName( - factory.getDialect(), - factory.getSettings().getDefaultCatalogName(), - factory.getSettings().getDefaultSchemaName() - ); + qualifiedTableNames[j] = determineTableName( join.getTable(), jdbcEnvironment ); isInverseTable[j] = join.isInverse(); isNullableTable[j] = join.isOptional(); cascadeDeleteEnabled[j] = join.getKey().isCascadeDeleteEnabled() && diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java index 4146f6d784..47c6912af2 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java @@ -19,10 +19,12 @@ import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.MappingException; +import org.hibernate.boot.model.relational.Database; import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy; import org.hibernate.cfg.Settings; import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.engine.spi.Mapping; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -74,8 +76,6 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { super( persistentClass, cacheAccessStrategy, naturalIdRegionAccessStrategy, creationContext ); - final SessionFactoryImplementor factory = creationContext.getSessionFactory(); - if ( getIdentifierGenerator() instanceof IdentityGenerator ) { throw new MappingException( "Cannot use identity column key generation with mapping for: " + @@ -83,18 +83,13 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { ); } + final SessionFactoryImplementor factory = creationContext.getSessionFactory(); + final Database database = creationContext.getMetadata().getDatabase(); + final JdbcEnvironment jdbcEnvironment = database.getJdbcEnvironment(); + // TABLE - tableName = persistentClass.getTable().getQualifiedName( - factory.getDialect(), - factory.getSettings().getDefaultCatalogName(), - factory.getSettings().getDefaultSchemaName() - ); - /*rootTableName = persistentClass.getRootTable().getQualifiedName( - factory.getDialect(), - factory.getDefaultCatalog(), - factory.getDefaultSchema() - );*/ + tableName = determineTableName( persistentClass.getTable(), jdbcEnvironment ); //Custom SQL @@ -173,14 +168,8 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { HashSet subclassTables = new HashSet(); iter = persistentClass.getSubclassTableClosureIterator(); while ( iter.hasNext() ) { - Table table = (Table) iter.next(); - subclassTables.add( - table.getQualifiedName( - factory.getDialect(), - factory.getSettings().getDefaultCatalogName(), - factory.getSettings().getDefaultSchemaName() - ) - ); + final Table table = (Table) iter.next(); + subclassTables.add( determineTableName( table, jdbcEnvironment ) ); } subclassSpaces = ArrayHelper.toStringArray( subclassTables ); @@ -198,11 +187,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { while ( iter.hasNext() ) { Table tab = (Table) iter.next(); if ( !tab.isAbstractUnionTable() ) { - String tableName = tab.getQualifiedName( - factory.getDialect(), - factory.getSettings().getDefaultCatalogName(), - factory.getSettings().getDefaultSchemaName() - ); + final String tableName = determineTableName( tab, jdbcEnvironment ); tableNames.add( tableName ); String[] key = new String[idColumnSpan]; Iterator citer = tab.getPrimaryKey().getColumnIterator(); diff --git a/hibernate-core/src/test/java/org/hibernate/test/boot/database/qualfiedTableNaming/JdbcMocks.java b/hibernate-core/src/test/java/org/hibernate/test/boot/database/qualfiedTableNaming/JdbcMocks.java new file mode 100644 index 0000000000..54d9a801c1 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/boot/database/qualfiedTableNaming/JdbcMocks.java @@ -0,0 +1,209 @@ +/* + * 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.boot.database.qualfiedTableNaming; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +/** + * @author Steve Ebersole + */ +public class JdbcMocks { + + public static Connection createConnection(String databaseName, int majorVersion) { + return createConnection( databaseName, majorVersion, -9999 ); + } + + public static Connection createConnection(String databaseName, int majorVersion, int minorVersion) { + DatabaseMetaDataHandler metadataHandler = new DatabaseMetaDataHandler( databaseName, majorVersion, minorVersion ); + ConnectionHandler connectionHandler = new ConnectionHandler(); + + DatabaseMetaData metadataProxy = ( DatabaseMetaData ) Proxy.newProxyInstance( + ClassLoader.getSystemClassLoader(), + new Class[] {DatabaseMetaData.class}, + metadataHandler + ); + + Connection connectionProxy = ( Connection ) Proxy.newProxyInstance( + ClassLoader.getSystemClassLoader(), + new Class[] { Connection.class }, + connectionHandler + ); + + metadataHandler.setConnectionProxy( connectionProxy ); + connectionHandler.setMetadataProxy( metadataProxy ); + + return connectionProxy; + } + + private static class ConnectionHandler implements InvocationHandler { + private DatabaseMetaData metadataProxy; + + public void setMetadataProxy(DatabaseMetaData metadataProxy) { + this.metadataProxy = metadataProxy; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + final String methodName = method.getName(); + if ( "getMetaData".equals( methodName ) ) { + return metadataProxy; + } + + if ( "toString".equals( methodName ) ) { + return "Connection proxy [@" + hashCode() + "]"; + } + + if ( "hashCode".equals( methodName ) ) { + return Integer.valueOf( this.hashCode() ); + } + + if ( "getCatalog".equals( methodName ) ) { + return "DB1"; + } + + if ( "supportsRefCursors".equals( methodName ) ) { + return false; + } + + if ( canThrowSQLException( method ) ) { + throw new SQLException(); + } + else { + throw new UnsupportedOperationException(); + } + } + } + + private static class DatabaseMetaDataHandler implements InvocationHandler { + private final String databaseName; + private final int majorVersion; + private final int minorVersion; + + private Connection connectionProxy; + + public void setConnectionProxy(Connection connectionProxy) { + this.connectionProxy = connectionProxy; + } + + private DatabaseMetaDataHandler(String databaseName, int majorVersion) { + this( databaseName, majorVersion, -9999 ); + } + + private DatabaseMetaDataHandler(String databaseName, int majorVersion, int minorVersion) { + this.databaseName = databaseName; + this.majorVersion = majorVersion; + this.minorVersion = minorVersion; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + final String methodName = method.getName(); + if ( "getDatabaseProductName".equals( methodName ) ) { + return databaseName; + } + + if ( "getDatabaseMajorVersion".equals( methodName ) ) { + return Integer.valueOf( majorVersion ); + } + + if ( "getDatabaseMinorVersion".equals( methodName ) ) { + return Integer.valueOf( minorVersion ); + } + + if ( "getConnection".equals( methodName ) ) { + return connectionProxy; + } + + if ( "toString".equals( methodName ) ) { + return "DatabaseMetaData proxy [db-name=" + databaseName + ", version=" + majorVersion + "]"; + } + + if ( "hashCode".equals( methodName ) ) { + return Integer.valueOf( this.hashCode() ); + } + + if ( "supportsNamedParameters".equals( methodName ) ) { + return true ; + } + + if ( "supportsResultSetType".equals( methodName ) ) { + return true ; + } + + if ( "supportsGetGeneratedKeys".equals( methodName ) ) { + return true ; + } + + if ( "supportsBatchUpdates".equals( methodName ) ) { + return true ; + } + + if ( "dataDefinitionIgnoredInTransactions".equals( methodName ) ) { + return false ; + } + + if ( "dataDefinitionCausesTransactionCommit".equals( methodName ) ) { + return false ; + } + + if ( "getSQLKeywords".equals( methodName ) ) { + return "after,ansi,append,attach,audit,before,bitmap,boolean,buffered,byte,cache,call,cluster,clustersize,codeset,database,datafiles,dataskip,datetime,dba,dbdate,dbmoney,debug,define,delimiter,deluxe,detach,dirty,distributions,document,each,elif,exclusive,exit,explain,express,expression,extend,extent,file,fillfactor,foreach,format,fraction,fragment,gk,hash,high,hold,hybrid,if,index,init,labeleq,labelge,labelgt,labelle,labellt,let,listing,lock,log,low,matches,maxerrors,medium,mode,modify,money,mounting,new,nvarchar,off,old,operational,optical,optimization,page,pdqpriority,pload,private,raise,range,raw,recordend,recover,referencing,rejectfile,release,remainder,rename,reserve,resolution,resource,resume,return,returning,returns,ridlist,robin,rollforward,round,row,rowids,sameas,samples,schedule,scratch,serial,share,skall,skinhibit,skshow,smallfloat,stability,standard,start,static,statistics,stdev,step,sync,synonym,system,temp,text,timeout,trace,trigger,units,unlock,variance,wait,while,xload,xunload" ; + } + + if ( "getSQLStateType".equals( methodName ) ) { + return DatabaseMetaData.sqlStateXOpen ; + } + + if ( "locatorsUpdateCopy".equals( methodName ) ) { + return false ; + } + + if ( "getTypeInfo".equals( methodName ) ) { + com.sun.rowset.CachedRowSetImpl rowSet = new com.sun.rowset.CachedRowSetImpl(); + return rowSet ; + } + + if ( "storesLowerCaseIdentifiers".equals( methodName ) ) { + return true ; + } + + if ( "storesUpperCaseIdentifiers".equals( methodName ) ) { + return false ; + } + + if ( "getCatalogSeparator".equals( methodName ) ) { + return ":" ; + } + + if ( "isCatalogAtStart".equals( methodName ) ) { + return true ; + } + + if ( canThrowSQLException( method ) ) { + throw new SQLException(); + } + else { + throw new UnsupportedOperationException(); + } + } + } + + private static boolean canThrowSQLException(Method method) { + final Class[] exceptions = method.getExceptionTypes(); + for ( Class exceptionType : exceptions ) { + if ( SQLException.class.isAssignableFrom( exceptionType ) ) { + return true; + } + } + return false; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/boot/database/qualfiedTableNaming/QualifiedTableNamingTest.java b/hibernate-core/src/test/java/org/hibernate/test/boot/database/qualfiedTableNaming/QualifiedTableNamingTest.java new file mode 100644 index 0000000000..c02271213d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/boot/database/qualfiedTableNaming/QualifiedTableNamingTest.java @@ -0,0 +1,129 @@ +/* + * 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.boot.database.qualfiedTableNaming; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Map; +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.hibernate.boot.model.naming.Identifier; +import org.hibernate.boot.model.relational.Namespace; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport; +import org.hibernate.mapping.Table; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.SingleTableEntityPersister; +import org.hibernate.tool.schema.internal.StandardTableExporter; + +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +/** + * @author Steve Ebersole + */ +public class QualifiedTableNamingTest extends BaseNonConfigCoreFunctionalTestCase { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Box.class }; + } + + @Override + protected boolean createSchema() { + return false; + } + + @Override + protected void addSettings(Map settings) { + super.addSettings( settings ); + settings.put( AvailableSettings.DIALECT, TestDialect.class ); + settings.put( AvailableSettings.CONNECTION_PROVIDER, MockedConnectionProvider.class.getName() ); + } + + @Test + public void testQualifiedNameSeparator() throws Exception { + Namespace.Name namespaceName = new Namespace.Name( + Identifier.toIdentifier( "DB1" ), + Identifier.toIdentifier( "PUBLIC" ) + ); + + String expectedName = null; + + for ( Namespace namespace : metadata().getDatabase().getNamespaces() ) { + if ( !namespace.getName().equals( namespaceName ) ) { + continue; + } + + assertEquals( 1, namespace.getTables().size() ); + + expectedName = metadata().getDatabase().getJdbcEnvironment().getQualifiedObjectNameFormatter().format( + namespace.getTables().iterator().next().getQualifiedTableName(), + getDialect() + ); + } + + assertNotNull( expectedName ); + + SingleTableEntityPersister persister = (SingleTableEntityPersister) sessionFactory().getEntityPersister( Box.class.getName() ); + assertEquals( expectedName, persister.getTableName() ); + } + + @Entity(name = "Box") + @javax.persistence.Table(name = "Box", schema = "PUBLIC", catalog = "DB1") + public static class Box { + @Id + public Integer id; + public String value; + } + + public static class TestDialect extends Dialect { + @Override + public NameQualifierSupport getNameQualifierSupport() { + return NameQualifierSupport.BOTH; + } + } + + public static class MockedConnectionProvider implements ConnectionProvider { + private Connection connection; + + @Override + public Connection getConnection() throws SQLException { + if (connection == null) { + connection = JdbcMocks.createConnection( "db1", 0 ); + } + return connection; + } + + @Override + public void closeConnection(Connection conn) throws SQLException { + } + + @Override + public boolean supportsAggressiveRelease() { + return false; + } + + + @Override + public boolean isUnwrappableAs(Class unwrapType) { + return false; + } + + @Override + public T unwrap(Class unwrapType) { + return null; + } + } + +}