org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentImpl.java

Source

/*
 * 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.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<TypeInfo> typeInfoSet = new LinkedHashSet<TypeInfo>();
    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<String> buildMergedReservedWords(Dialect dialect, DatabaseMetaData dbmd) throws SQLException {
        Set<String> reservedWords = new HashSet<String>();
        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<SequenceInformation> sequenceInformationList(final Connection connection) {
        try {

            Iterable<SequenceInformation> 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();
    }
}