org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-core Show documentation
Show all versions of hibernate-core Show documentation
Hibernate's core ORM functionality
/*
* 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.engine.jdbc.env.internal;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.registry.selector.spi.StrategySelector;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.engine.jdbc.env.spi.ExtractedDatabaseMetaData;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.env.spi.LobCreatorBuilder;
import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport;
import org.hibernate.engine.jdbc.env.spi.QualifiedObjectNameFormatter;
import org.hibernate.engine.jdbc.env.spi.SchemaNameResolver;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
import org.hibernate.engine.jdbc.spi.TypeInfo;
import org.hibernate.exception.internal.SQLExceptionTypeDelegate;
import org.hibernate.exception.internal.SQLStateConversionDelegate;
import org.hibernate.exception.internal.StandardSQLExceptionConverter;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.tool.schema.extract.spi.ExtractionContext;
import org.hibernate.tool.schema.extract.spi.SequenceInformation;
import org.jboss.logging.Logger;
/**
* @author Steve Ebersole
*/
public class JdbcEnvironmentImpl implements JdbcEnvironment {
private static final Logger log = Logger.getLogger( JdbcEnvironmentImpl.class );
private final Dialect dialect;
private final SqlExceptionHelper sqlExceptionHelper;
private final ExtractedDatabaseMetaData extractedMetaDataSupport;
private final Identifier currentCatalog;
private final Identifier currentSchema;
private final IdentifierHelper identifierHelper;
private final QualifiedObjectNameFormatter qualifiedObjectNameFormatter;
private final LobCreatorBuilderImpl lobCreatorBuilder;
private final LinkedHashSet typeInfoSet = new LinkedHashSet();
private final NameQualifierSupport nameQualifierSupport;
/**
* Constructor form used when the JDBC {@link java.sql.DatabaseMetaData} is not available.
*
* @param serviceRegistry The service registry
* @param dialect The resolved dialect.
*/
public JdbcEnvironmentImpl(final ServiceRegistryImplementor serviceRegistry, Dialect dialect) {
this.dialect = dialect;
final ConfigurationService cfgService = serviceRegistry.getService( ConfigurationService.class );
NameQualifierSupport nameQualifierSupport = dialect.getNameQualifierSupport();
if ( nameQualifierSupport == null ) {
// assume both catalogs and schemas are supported
nameQualifierSupport = NameQualifierSupport.BOTH;
}
this.nameQualifierSupport = nameQualifierSupport;
this.sqlExceptionHelper = buildSqlExceptionHelper( dialect, logWarnings( cfgService, dialect ) );
final IdentifierHelperBuilder identifierHelperBuilder = IdentifierHelperBuilder.from( this );
identifierHelperBuilder.setGloballyQuoteIdentifiers( globalQuoting( cfgService ) );
identifierHelperBuilder.setSkipGlobalQuotingForColumnDefinitions( globalQuotingSkippedForColumnDefinitions(
cfgService ) );
identifierHelperBuilder.setAutoQuoteKeywords( autoKeywordQuoting( cfgService ) );
identifierHelperBuilder.setNameQualifierSupport( nameQualifierSupport );
IdentifierHelper identifierHelper = null;
ExtractedDatabaseMetaDataImpl.Builder dbMetaDataBuilder = new ExtractedDatabaseMetaDataImpl.Builder( this );
try {
identifierHelper = dialect.buildIdentifierHelper( identifierHelperBuilder, null );
dbMetaDataBuilder.setSupportsNamedParameters( dialect.supportsNamedParameters( null ) );
}
catch (SQLException sqle) {
// should never ever happen
log.debug( "There was a problem accessing DatabaseMetaData in building the JdbcEnvironment", sqle );
}
if ( identifierHelper == null ) {
identifierHelper = identifierHelperBuilder.build();
}
this.identifierHelper = identifierHelper;
this.extractedMetaDataSupport = dbMetaDataBuilder.build();
this.currentCatalog = identifierHelper.toIdentifier(
cfgService.getSetting( AvailableSettings.DEFAULT_CATALOG, StandardConverters.STRING )
);
this.currentSchema = Identifier.toIdentifier(
cfgService.getSetting( AvailableSettings.DEFAULT_SCHEMA, StandardConverters.STRING )
);
this.qualifiedObjectNameFormatter = new QualifiedObjectNameFormatterStandardImpl( nameQualifierSupport );
this.lobCreatorBuilder = LobCreatorBuilderImpl.makeLobCreatorBuilder();
}
private static boolean logWarnings(ConfigurationService cfgService, Dialect dialect) {
return cfgService.getSetting(
AvailableSettings.LOG_JDBC_WARNINGS,
StandardConverters.BOOLEAN,
dialect.isJdbcLogWarningsEnabledByDefault()
);
}
private static boolean globalQuoting(ConfigurationService cfgService) {
return cfgService.getSetting(
AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS,
StandardConverters.BOOLEAN,
false
);
}
private boolean globalQuotingSkippedForColumnDefinitions(ConfigurationService cfgService) {
return cfgService.getSetting(
AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS_SKIP_COLUMN_DEFINITIONS,
StandardConverters.BOOLEAN,
false
);
}
private static boolean autoKeywordQuoting(ConfigurationService cfgService) {
return cfgService.getSetting(
AvailableSettings.KEYWORD_AUTO_QUOTING_ENABLED,
StandardConverters.BOOLEAN,
false
);
}
/**
* Constructor form used from testing
*
* @param dialect The dialect
*/
public JdbcEnvironmentImpl(DatabaseMetaData databaseMetaData, Dialect dialect) throws SQLException {
this.dialect = dialect;
this.sqlExceptionHelper = buildSqlExceptionHelper( dialect, false );
NameQualifierSupport nameQualifierSupport = dialect.getNameQualifierSupport();
if ( nameQualifierSupport == null ) {
nameQualifierSupport = determineNameQualifierSupport( databaseMetaData );
}
this.nameQualifierSupport = nameQualifierSupport;
final IdentifierHelperBuilder identifierHelperBuilder = IdentifierHelperBuilder.from( this );
identifierHelperBuilder.setNameQualifierSupport( nameQualifierSupport );
IdentifierHelper identifierHelper = null;
try {
identifierHelper = dialect.buildIdentifierHelper( identifierHelperBuilder, databaseMetaData );
}
catch (SQLException sqle) {
// should never ever happen
log.debug( "There was a problem accessing DatabaseMetaData in building the JdbcEnvironment", sqle );
}
if ( identifierHelper == null ) {
identifierHelper = identifierHelperBuilder.build();
}
this.identifierHelper = identifierHelper;
this.extractedMetaDataSupport = new ExtractedDatabaseMetaDataImpl.Builder( this )
.apply( databaseMetaData )
.setSupportsNamedParameters( databaseMetaData.supportsNamedParameters() )
.setSequenceInformationList( sequenceInformationList( databaseMetaData.getConnection() ) )
.build();
this.currentCatalog = null;
this.currentSchema = null;
this.qualifiedObjectNameFormatter = new QualifiedObjectNameFormatterStandardImpl(
nameQualifierSupport,
databaseMetaData
);
this.lobCreatorBuilder = LobCreatorBuilderImpl.makeLobCreatorBuilder();
}
private NameQualifierSupport determineNameQualifierSupport(DatabaseMetaData databaseMetaData) throws SQLException {
final boolean supportsCatalogs = databaseMetaData.supportsCatalogsInTableDefinitions();
final boolean supportsSchemas = databaseMetaData.supportsSchemasInTableDefinitions();
if ( supportsCatalogs && supportsSchemas ) {
return NameQualifierSupport.BOTH;
}
else if ( supportsCatalogs ) {
return NameQualifierSupport.CATALOG;
}
else if ( supportsSchemas ) {
return NameQualifierSupport.SCHEMA;
}
else {
return NameQualifierSupport.NONE;
}
}
/**
* The main constructor form. Builds a JdbcEnvironment using the available DatabaseMetaData
*
* @param serviceRegistry The service registry
* @param dialect The resolved dialect
* @param databaseMetaData The available DatabaseMetaData
*
* @throws SQLException
*/
public JdbcEnvironmentImpl(
ServiceRegistryImplementor serviceRegistry,
Dialect dialect,
DatabaseMetaData databaseMetaData) throws SQLException {
this.dialect = dialect;
final ConfigurationService cfgService = serviceRegistry.getService( ConfigurationService.class );
this.sqlExceptionHelper = buildSqlExceptionHelper( dialect, logWarnings( cfgService, dialect ) );
NameQualifierSupport nameQualifierSupport = dialect.getNameQualifierSupport();
if ( nameQualifierSupport == null ) {
nameQualifierSupport = determineNameQualifierSupport( databaseMetaData );
}
this.nameQualifierSupport = nameQualifierSupport;
final IdentifierHelperBuilder identifierHelperBuilder = IdentifierHelperBuilder.from( this );
identifierHelperBuilder.setGloballyQuoteIdentifiers( globalQuoting( cfgService ) );
identifierHelperBuilder.setSkipGlobalQuotingForColumnDefinitions( globalQuotingSkippedForColumnDefinitions(
cfgService ) );
identifierHelperBuilder.setAutoQuoteKeywords( autoKeywordQuoting( cfgService ) );
identifierHelperBuilder.setNameQualifierSupport( nameQualifierSupport );
IdentifierHelper identifierHelper = null;
try {
identifierHelper = dialect.buildIdentifierHelper( identifierHelperBuilder, databaseMetaData );
}
catch (SQLException sqle) {
// should never ever happen
log.debug( "There was a problem accessing DatabaseMetaData in building the JdbcEnvironment", sqle );
}
if ( identifierHelper == null ) {
identifierHelper = identifierHelperBuilder.build();
}
this.identifierHelper = identifierHelper;
this.extractedMetaDataSupport = new ExtractedDatabaseMetaDataImpl.Builder( this )
.apply( databaseMetaData )
.setConnectionSchemaName( determineCurrentSchemaName( databaseMetaData, serviceRegistry, dialect ) )
.setSupportsNamedParameters( dialect.supportsNamedParameters( databaseMetaData ) )
.setSequenceInformationList( sequenceInformationList( databaseMetaData.getConnection() ) )
.build();
// and that current-catalog and current-schema happen after it
this.currentCatalog = identifierHelper.toIdentifier( extractedMetaDataSupport.getConnectionCatalogName() );
this.currentSchema = identifierHelper.toIdentifier( extractedMetaDataSupport.getConnectionSchemaName() );
this.qualifiedObjectNameFormatter = new QualifiedObjectNameFormatterStandardImpl(
nameQualifierSupport,
databaseMetaData
);
this.typeInfoSet.addAll( TypeInfo.extractTypeInfo( databaseMetaData ) );
this.lobCreatorBuilder = LobCreatorBuilderImpl.makeLobCreatorBuilder(
dialect,
cfgService.getSettings(),
databaseMetaData.getConnection()
);
}
public static final String SCHEMA_NAME_RESOLVER = "hibernate.schema_name_resolver";
private String determineCurrentSchemaName(
DatabaseMetaData databaseMetaData,
ServiceRegistry serviceRegistry,
Dialect dialect) throws SQLException {
final SchemaNameResolver schemaNameResolver;
final Object setting = serviceRegistry.getService( ConfigurationService.class ).getSettings().get(
SCHEMA_NAME_RESOLVER );
if ( setting == null ) {
schemaNameResolver = dialect.getSchemaNameResolver();
}
else {
schemaNameResolver = serviceRegistry.getService( StrategySelector.class ).resolveDefaultableStrategy(
SchemaNameResolver.class,
setting,
dialect.getSchemaNameResolver()
);
}
try {
return schemaNameResolver.resolveSchemaName( databaseMetaData.getConnection(), dialect );
}
catch (Exception e) {
log.debug( "Unable to resolve connection default schema", e );
return null;
}
}
@SuppressWarnings("deprecation")
private SqlExceptionHelper buildSqlExceptionHelper(Dialect dialect, boolean logWarnings) {
final StandardSQLExceptionConverter sqlExceptionConverter = new StandardSQLExceptionConverter();
sqlExceptionConverter.addDelegate( dialect.buildSQLExceptionConversionDelegate() );
sqlExceptionConverter.addDelegate( new SQLExceptionTypeDelegate( dialect ) );
// todo : vary this based on extractedMetaDataSupport.getSqlStateType()
sqlExceptionConverter.addDelegate( new SQLStateConversionDelegate( dialect ) );
return new SqlExceptionHelper( sqlExceptionConverter, logWarnings );
}
private Set buildMergedReservedWords(Dialect dialect, DatabaseMetaData dbmd) throws SQLException {
Set reservedWords = new HashSet();
reservedWords.addAll( dialect.getKeywords() );
// todo : do we need to explicitly handle SQL:2003 keywords?
reservedWords.addAll( Arrays.asList( dbmd.getSQLKeywords().split( "," ) ) );
return reservedWords;
}
@Override
public Dialect getDialect() {
return dialect;
}
@Override
public ExtractedDatabaseMetaData getExtractedDatabaseMetaData() {
return extractedMetaDataSupport;
}
@Override
public Identifier getCurrentCatalog() {
return currentCatalog;
}
@Override
public Identifier getCurrentSchema() {
return currentSchema;
}
@Override
public QualifiedObjectNameFormatter getQualifiedObjectNameFormatter() {
return qualifiedObjectNameFormatter;
}
@Override
public IdentifierHelper getIdentifierHelper() {
return identifierHelper;
}
@Override
public NameQualifierSupport getNameQualifierSupport() {
return nameQualifierSupport;
}
@Override
public SqlExceptionHelper getSqlExceptionHelper() {
return sqlExceptionHelper;
}
@Override
public LobCreatorBuilder getLobCreatorBuilder() {
return lobCreatorBuilder;
}
@Override
public TypeInfo getTypeInfoForJdbcCode(int jdbcTypeCode) {
for ( TypeInfo typeInfo : typeInfoSet ) {
if ( typeInfo.getJdbcTypeCode() == jdbcTypeCode ) {
return typeInfo;
}
}
return null;
}
/**
* Get the sequence information List from the database.
*
* @param connection database connection
* @return sequence information List
*/
private List sequenceInformationList(final Connection connection) {
try {
Iterable sequenceInformationIterable = dialect
.getSequenceInformationExtractor()
.extractMetadata( new ExtractionContext.EmptyExtractionContext() {
@Override
public Connection getJdbcConnection() {
return connection;
}
@Override
public JdbcEnvironment getJdbcEnvironment() {
return JdbcEnvironmentImpl.this;
}
}
);
return StreamSupport.stream( sequenceInformationIterable.spliterator(), false )
.collect( Collectors.toList() );
}
catch (SQLException e) {
log.error( "Could not fetch the SequenceInformation from the database", e );
}
return Collections.emptyList();
}
}