HHH-9848 - Allow disabling auto-quoting of database object names (tables, columns, etc)

This commit is contained in:
Steve Ebersole 2015-06-05 13:43:10 -05:00
parent 5b1da92498
commit 30b260f14f
9 changed files with 118 additions and 22 deletions

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.boot; package org.hibernate.boot;
import java.sql.DatabaseMetaData;
import javax.persistence.AttributeConverter; import javax.persistence.AttributeConverter;
import javax.persistence.SharedCacheMode; import javax.persistence.SharedCacheMode;
@ -23,6 +24,7 @@ import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.cfg.AttributeConverterDefinition; import org.hibernate.cfg.AttributeConverterDefinition;
import org.hibernate.cfg.MetadataSourceType; import org.hibernate.cfg.MetadataSourceType;
import org.hibernate.dialect.function.SQLFunction; import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.usertype.CompositeUserType; import org.hibernate.usertype.CompositeUserType;
import org.hibernate.usertype.UserType; import org.hibernate.usertype.UserType;

View File

@ -540,6 +540,8 @@ public class MetadataBuilderImpl implements MetadataBuilder, TypeContributions {
private IdGeneratorInterpreterImpl idGenerationTypeInterpreter = new IdGeneratorInterpreterImpl(); private IdGeneratorInterpreterImpl idGenerationTypeInterpreter = new IdGeneratorInterpreterImpl();
private boolean autoQuoteKeywords;
// private PersistentAttributeMemberResolver persistentAttributeMemberResolver = // private PersistentAttributeMemberResolver persistentAttributeMemberResolver =
// StandardPersistentAttributeMemberResolver.INSTANCE; // StandardPersistentAttributeMemberResolver.INSTANCE;

View File

@ -18,24 +18,24 @@ import org.hibernate.cache.spi.access.AccessType;
* @since 5.0 * @since 5.0
*/ */
public interface MappingDefaults { public interface MappingDefaults {
public static final String DEFAULT_IDENTIFIER_COLUMN_NAME = "id"; String DEFAULT_IDENTIFIER_COLUMN_NAME = "id";
public static final String DEFAULT_TENANT_IDENTIFIER_COLUMN_NAME = "tenant_id"; String DEFAULT_TENANT_IDENTIFIER_COLUMN_NAME = "tenant_id";
public static final String DEFAULT_DISCRIMINATOR_COLUMN_NAME = "class"; String DEFAULT_DISCRIMINATOR_COLUMN_NAME = "class";
public static final String DEFAULT_CASCADE_NAME = "none"; String DEFAULT_CASCADE_NAME = "none";
public static final String DEFAULT_PROPERTY_ACCESS_NAME = "property"; String DEFAULT_PROPERTY_ACCESS_NAME = "property";
/** /**
* Identifies the database schema name to use if none specified in the mapping. * Identifies the database schema name to use if none specified in the mapping.
* *
* @return The implicit schema name; may be {@code null} * @return The implicit schema name; may be {@code null}
*/ */
public String getImplicitSchemaName(); String getImplicitSchemaName();
/** /**
* Identifies the database catalog name to use if none specified in the mapping. * Identifies the database catalog name to use if none specified in the mapping.
* *
* @return The implicit catalog name; may be {@code null} * @return The implicit catalog name; may be {@code null}
*/ */
public String getImplicitCatalogName(); String getImplicitCatalogName();
/** /**
* Should all database identifiers encountered in this context be implicitly quoted? * Should all database identifiers encountered in this context be implicitly quoted?
@ -46,7 +46,7 @@ public interface MappingDefaults {
* *
* @return {@code true}/{@code false} * @return {@code true}/{@code false}
*/ */
public boolean shouldImplicitlyQuoteIdentifiers(); boolean shouldImplicitlyQuoteIdentifiers();
/** /**
* Identifies the column name to use for the identifier column if none specified in * Identifies the column name to use for the identifier column if none specified in
@ -54,7 +54,7 @@ public interface MappingDefaults {
* *
* @return The implicit identifier column name * @return The implicit identifier column name
*/ */
public String getImplicitIdColumnName(); String getImplicitIdColumnName();
/** /**
* Identifies the column name to use for the tenant identifier column if none is * Identifies the column name to use for the tenant identifier column if none is
@ -62,7 +62,7 @@ public interface MappingDefaults {
* *
* @return The implicit tenant identifier column name * @return The implicit tenant identifier column name
*/ */
public String getImplicitTenantIdColumnName(); String getImplicitTenantIdColumnName();
/** /**
* Identifies the column name to use for the discriminator column if none specified * Identifies the column name to use for the discriminator column if none specified
@ -70,7 +70,7 @@ public interface MappingDefaults {
* *
* @return The implicit discriminator column name * @return The implicit discriminator column name
*/ */
public String getImplicitDiscriminatorColumnName(); String getImplicitDiscriminatorColumnName();
/** /**
* Identifies the package name to use if none specified in the mapping. Really only * Identifies the package name to use if none specified in the mapping. Really only
@ -78,21 +78,21 @@ public interface MappingDefaults {
* *
* @return The implicit package name. * @return The implicit package name.
*/ */
public String getImplicitPackageName(); String getImplicitPackageName();
/** /**
* Is auto-importing of entity (short) names enabled? * Is auto-importing of entity (short) names enabled?
* *
* @return {@code true} if auto-importing is enabled; {@code false} otherwise. * @return {@code true} if auto-importing is enabled; {@code false} otherwise.
*/ */
public boolean isAutoImportEnabled(); boolean isAutoImportEnabled();
/** /**
* Identifies the cascade style to apply to associations if none specified in the mapping. * Identifies the cascade style to apply to associations if none specified in the mapping.
* *
* @return The implicit cascade style * @return The implicit cascade style
*/ */
public String getImplicitCascadeStyleName(); String getImplicitCascadeStyleName();
/** /**
* Identifies the default {@link org.hibernate.property.access.spi.PropertyAccessStrategy} name to use if none specified in the * Identifies the default {@link org.hibernate.property.access.spi.PropertyAccessStrategy} name to use if none specified in the
@ -102,7 +102,7 @@ public interface MappingDefaults {
* *
* @see org.hibernate.property.access.spi.PropertyAccessStrategy * @see org.hibernate.property.access.spi.PropertyAccessStrategy
*/ */
public String getImplicitPropertyAccessorName(); String getImplicitPropertyAccessorName();
/** /**
* Identifies whether singular associations (many-to-one, one-to-one) are lazy * Identifies whether singular associations (many-to-one, one-to-one) are lazy
@ -110,19 +110,19 @@ public interface MappingDefaults {
* *
* @return The implicit association laziness * @return The implicit association laziness
*/ */
public boolean areEntitiesImplicitlyLazy(); boolean areEntitiesImplicitlyLazy();
/** /**
* Identifies whether plural attributes are lazy by default if not specified in the mapping. * Identifies whether plural attributes are lazy by default if not specified in the mapping.
* *
* @return The implicit association laziness * @return The implicit association laziness
*/ */
public boolean areCollectionsImplicitlyLazy(); boolean areCollectionsImplicitlyLazy();
/** /**
* The cache access type to use if none is specified * The cache access type to use if none is specified
* *
* @return The implicit cache access type. * @return The implicit cache access type.
*/ */
public AccessType getImplicitCacheAccessType(); AccessType getImplicitCacheAccessType();
} }

View File

@ -746,9 +746,11 @@ public interface AvailableSettings {
*/ */
String LOG_SESSION_METRICS = "hibernate.session.events.log"; String LOG_SESSION_METRICS = "hibernate.session.events.log";
/**
* Defines a default {@link org.hibernate.SessionEventListener} to be applied to opened Sessions.
*/
String AUTO_SESSION_EVENTS_LISTENER = "hibernate.session.events.auto"; String AUTO_SESSION_EVENTS_LISTENER = "hibernate.session.events.auto";
/** /**
* The deprecated name. Use {@link #SCANNER} or {@link #SCANNER_ARCHIVE_INTERPRETER} instead. * The deprecated name. Use {@link #SCANNER} or {@link #SCANNER_ARCHIVE_INTERPRETER} instead.
*/ */
@ -820,4 +822,12 @@ public interface AvailableSettings {
* annotations (combined with {@code orm.xml} mappings). * annotations (combined with {@code orm.xml} mappings).
*/ */
String ARTIFACT_PROCESSING_ORDER = "hibernate.mapping.precedence"; String ARTIFACT_PROCESSING_ORDER = "hibernate.mapping.precedence";
/**
* Specifies whether to automatically quote any names that are deemed keywords. Auto-quoting
* is enabled by default. Set to false to disable.
*
* @since 5.0
*/
String KEYWORD_AUTO_QUOTING_ENABLED = "hibernate.auto_quote_keyword";
} }

View File

@ -20,7 +20,6 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData; import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData;
import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
@ -78,6 +77,7 @@ public class JdbcEnvironmentImpl implements JdbcEnvironment {
final IdentifierHelperBuilder identifierHelperBuilder = IdentifierHelperBuilder.from( this ); final IdentifierHelperBuilder identifierHelperBuilder = IdentifierHelperBuilder.from( this );
identifierHelperBuilder.setGloballyQuoteIdentifiers( globalQuoting( cfgService ) ); identifierHelperBuilder.setGloballyQuoteIdentifiers( globalQuoting( cfgService ) );
identifierHelperBuilder.setGloballyQuoteIdentifiers( autoQuoting( cfgService ) );
identifierHelperBuilder.setNameQualifierSupport( nameQualifierSupport ); identifierHelperBuilder.setNameQualifierSupport( nameQualifierSupport );
IdentifierHelper identifierHelper = null; IdentifierHelper identifierHelper = null;
@ -113,6 +113,14 @@ public class JdbcEnvironmentImpl implements JdbcEnvironment {
); );
} }
private static boolean autoQuoting(ConfigurationService cfgService) {
return cfgService.getSetting(
AvailableSettings.KEYWORD_AUTO_QUOTING_ENABLED,
StandardConverters.BOOLEAN,
true
);
}
/** /**
* Constructor form used from testing * Constructor form used from testing
* *
@ -207,6 +215,7 @@ public class JdbcEnvironmentImpl implements JdbcEnvironment {
final IdentifierHelperBuilder identifierHelperBuilder = IdentifierHelperBuilder.from( this ); final IdentifierHelperBuilder identifierHelperBuilder = IdentifierHelperBuilder.from( this );
identifierHelperBuilder.setGloballyQuoteIdentifiers( globalQuoting( cfgService ) ); identifierHelperBuilder.setGloballyQuoteIdentifiers( globalQuoting( cfgService ) );
identifierHelperBuilder.setAutoQuoteKeywords( autoQuoting( cfgService ) );
identifierHelperBuilder.setNameQualifierSupport( nameQualifierSupport ); identifierHelperBuilder.setNameQualifierSupport( nameQualifierSupport );
IdentifierHelper identifierHelper = null; IdentifierHelper identifierHelper = null;
try { try {

View File

@ -28,6 +28,7 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper {
private final NameQualifierSupport nameQualifierSupport; private final NameQualifierSupport nameQualifierSupport;
private final boolean globallyQuoteIdentifiers; private final boolean globallyQuoteIdentifiers;
private final boolean autoQuoteKeywords;
private final Set<String> reservedWords = new TreeSet<String>( String.CASE_INSENSITIVE_ORDER ); private final Set<String> reservedWords = new TreeSet<String>( String.CASE_INSENSITIVE_ORDER );
private final IdentifierCaseStrategy unquotedCaseStrategy; private final IdentifierCaseStrategy unquotedCaseStrategy;
private final IdentifierCaseStrategy quotedCaseStrategy; private final IdentifierCaseStrategy quotedCaseStrategy;
@ -36,12 +37,14 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper {
JdbcEnvironment jdbcEnvironment, JdbcEnvironment jdbcEnvironment,
NameQualifierSupport nameQualifierSupport, NameQualifierSupport nameQualifierSupport,
boolean globallyQuoteIdentifiers, boolean globallyQuoteIdentifiers,
boolean autoQuoteKeywords,
Set<String> reservedWords, Set<String> reservedWords,
IdentifierCaseStrategy unquotedCaseStrategy, IdentifierCaseStrategy unquotedCaseStrategy,
IdentifierCaseStrategy quotedCaseStrategy) { IdentifierCaseStrategy quotedCaseStrategy) {
this.jdbcEnvironment = jdbcEnvironment; this.jdbcEnvironment = jdbcEnvironment;
this.nameQualifierSupport = nameQualifierSupport; this.nameQualifierSupport = nameQualifierSupport;
this.globallyQuoteIdentifiers = globallyQuoteIdentifiers; this.globallyQuoteIdentifiers = globallyQuoteIdentifiers;
this.autoQuoteKeywords = autoQuoteKeywords;
if ( reservedWords != null ) { if ( reservedWords != null ) {
this.reservedWords.addAll( reservedWords ); this.reservedWords.addAll( reservedWords );
} }
@ -66,7 +69,7 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper {
return Identifier.toIdentifier( identifier.getText(), true ); return Identifier.toIdentifier( identifier.getText(), true );
} }
if ( isReservedWord( identifier.getText() ) ) { if ( autoQuoteKeywords && isReservedWord( identifier.getText() ) ) {
log.tracef( "Forcing identifier [%s] to quoted as recognized reserveed word", identifier ); log.tracef( "Forcing identifier [%s] to quoted as recognized reserveed word", identifier );
return Identifier.toIdentifier( identifier.getText(), true ); return Identifier.toIdentifier( identifier.getText(), true );
} }
@ -208,7 +211,7 @@ public class NormalizingIdentifierHelperImpl implements IdentifierHelper {
return Identifier.toIdentifier( text, true ); return Identifier.toIdentifier( text, true );
} }
if ( isReservedWord( text ) ) { if ( autoQuoteKeywords && isReservedWord( text ) ) {
// unequivocally it needs to be quoted... // unequivocally it needs to be quoted...
log.trace( "Forcing DatabaseMetaData return value as quoted as it was recognized as a reserved word" ); log.trace( "Forcing DatabaseMetaData return value as quoted as it was recognized as a reserved word" );
return Identifier.toIdentifier( text, true ); return Identifier.toIdentifier( text, true );

View File

@ -34,6 +34,7 @@ public class IdentifierHelperBuilder {
private Set<String> reservedWords = new TreeSet<String>( String.CASE_INSENSITIVE_ORDER ); private Set<String> reservedWords = new TreeSet<String>( String.CASE_INSENSITIVE_ORDER );
private boolean globallyQuoteIdentifiers = false; private boolean globallyQuoteIdentifiers = false;
private boolean autoQuoteKeywords = true;
private IdentifierCaseStrategy unquotedCaseStrategy = IdentifierCaseStrategy.UPPER; private IdentifierCaseStrategy unquotedCaseStrategy = IdentifierCaseStrategy.UPPER;
private IdentifierCaseStrategy quotedCaseStrategy = IdentifierCaseStrategy.MIXED; private IdentifierCaseStrategy quotedCaseStrategy = IdentifierCaseStrategy.MIXED;
@ -135,6 +136,10 @@ public class IdentifierHelperBuilder {
this.globallyQuoteIdentifiers = globallyQuoteIdentifiers; this.globallyQuoteIdentifiers = globallyQuoteIdentifiers;
} }
public void setAutoQuoteKeywords(boolean autoQuoteKeywords) {
this.autoQuoteKeywords = autoQuoteKeywords;
}
public NameQualifierSupport getNameQualifierSupport() { public NameQualifierSupport getNameQualifierSupport() {
return nameQualifierSupport; return nameQualifierSupport;
} }
@ -186,6 +191,7 @@ public class IdentifierHelperBuilder {
jdbcEnvironment, jdbcEnvironment,
nameQualifierSupport, nameQualifierSupport,
globallyQuoteIdentifiers, globallyQuoteIdentifiers,
autoQuoteKeywords,
reservedWords, reservedWords,
unquotedCaseStrategy, unquotedCaseStrategy,
quotedCaseStrategy quotedCaseStrategy

View File

@ -0,0 +1,51 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.schemaupdate;
import java.util.Collections;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.testing.boot.ServiceRegistryTestingImpl;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Steve Ebersole
*/
public class IdentifierHelperTest extends BaseUnitTestCase {
@Test
public void testAutoQuotingDisabled() {
ServiceRegistry sr = ServiceRegistryTestingImpl.forUnitTesting(
Collections.singletonMap(
AvailableSettings.KEYWORD_AUTO_QUOTING_ENABLED,
// true is the default, but to be sure...
true
)
);
Identifier identifier = sr.getService( JdbcEnvironment.class ).getIdentifierHelper().toIdentifier( "select" );
assertTrue( identifier.isQuoted() );
StandardServiceRegistryBuilder.destroy( sr );
sr = ServiceRegistryTestingImpl.forUnitTesting(
Collections.singletonMap(
AvailableSettings.KEYWORD_AUTO_QUOTING_ENABLED,
false
)
);
identifier = sr.getService( JdbcEnvironment.class ).getIdentifierHelper().toIdentifier( "select" );
assertFalse( identifier.isQuoted() );
StandardServiceRegistryBuilder.destroy( sr );
}
}

View File

@ -45,6 +45,19 @@ public class ServiceRegistryTestingImpl
); );
} }
public static ServiceRegistryTestingImpl forUnitTesting(Map settings) {
return new ServiceRegistryTestingImpl(
true,
new BootstrapServiceRegistryBuilder().build(),
StandardServiceInitiators.LIST,
Arrays.asList(
dialectFactoryService(),
connectionProviderService()
),
settings
);
}
private static ProvidedService dialectFactoryService() { private static ProvidedService dialectFactoryService() {
return new ProvidedService<DialectFactory>( DialectFactory.class, new DialectFactoryTestingImpl() ); return new ProvidedService<DialectFactory>( DialectFactory.class, new DialectFactoryTestingImpl() );
} }