diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/spi/StrategyCreator.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/spi/StrategyCreator.java index b561594491..3947251a8f 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/spi/StrategyCreator.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/spi/StrategyCreator.java @@ -9,6 +9,7 @@ /** * @author Steve Ebersole */ +@FunctionalInterface public interface StrategyCreator { T create(Class strategyClass); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java index b108bbb5a7..577ebd18a9 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/connections/internal/DriverManagerConnectionProviderImpl.java @@ -8,7 +8,9 @@ import java.sql.Connection; import java.sql.Driver; +import java.sql.DriverManager; import java.sql.SQLException; +import java.util.Enumeration; import java.util.Map; import java.util.Properties; import java.util.concurrent.ConcurrentLinkedQueue; @@ -22,6 +24,7 @@ import org.hibernate.HibernateException; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.Database; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; @@ -101,10 +104,51 @@ private PooledConnections buildPool(Map configurationValues, ServiceRegistryImpl private static ConnectionCreator buildCreator(Map configurationValues, ServiceRegistryImplementor serviceRegistry) { final ConnectionCreatorBuilder connectionCreatorBuilder = new ConnectionCreatorBuilder( serviceRegistry ); - final String driverClassName = (String) configurationValues.get( AvailableSettings.DRIVER ); - connectionCreatorBuilder.setDriver( loadDriverIfPossible( driverClassName, serviceRegistry ) ); - final String url = (String) configurationValues.get( AvailableSettings.URL ); + + String driverClassName = (String) configurationValues.get( AvailableSettings.DRIVER ); + boolean success = false; + if ( driverClassName != null ) { + connectionCreatorBuilder.setDriver( loadDriverIfPossible( driverClassName, serviceRegistry ) ); + success = true; + } + else if ( url != null ) { + //try to guess the driver class from the JDBC URL + for ( Database database: Database.values() ) { + if ( database.matchesUrl( url ) ) { + driverClassName = database.getDriverClassName( url ); + if ( driverClassName != null ) { + try { + connectionCreatorBuilder.setDriver( loadDriverIfPossible(driverClassName, serviceRegistry) ); + success = true; + } + catch (Exception e) { + //swallow it, since this was not + //an explicit setting by the user + } + break; + } + } + } + } + + if ( success ) { + log.loadedDriver( driverClassName ); + } + else { + //we're hoping that the driver is already loaded + log.noDriver( AvailableSettings.DRIVER ); + StringBuilder list = new StringBuilder(); + Enumeration drivers = DriverManager.getDrivers(); + while ( drivers.hasMoreElements() ) { + if ( list.length() != 0) { + list.append(", "); + } + list.append( drivers.nextElement().getClass().getName() ); + } + log.loadedDrivers( list.toString() ); + } + if ( url == null ) { final String msg = log.jdbcUrlNotSpecified( AvailableSettings.URL ); log.error( msg ); @@ -112,7 +156,7 @@ private static ConnectionCreator buildCreator(Map configurationValues, ServiceRe } connectionCreatorBuilder.setUrl( url ); - log.usingDriver( driverClassName, url ); + log.usingUrl( url ); final Properties connectionProps = ConnectionProviderInitiator.getConnectionProperties( configurationValues ); @@ -217,7 +261,6 @@ protected void finalize() throws Throwable { /** * Exposed to facilitate testing only. - * @return */ public Properties getConnectionProperties() { BasicConnectionCreator connectionCreator = (BasicConnectionCreator) this.state.pool.connectionCreator; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/dialect/internal/DialectFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/dialect/internal/DialectFactoryImpl.java index 84762c0e71..c773974fd5 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/dialect/internal/DialectFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/dialect/internal/DialectFactoryImpl.java @@ -6,10 +6,12 @@ */ package org.hibernate.engine.jdbc.dialect.internal; +import java.lang.reflect.InvocationTargetException; import java.util.Map; import org.hibernate.HibernateException; import org.hibernate.annotations.common.util.StringHelper; +import org.hibernate.boot.registry.selector.spi.StrategySelectionException; import org.hibernate.boot.registry.selector.spi.StrategySelector; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.Dialect; @@ -48,7 +50,7 @@ public void setDialectResolver(DialectResolver dialectResolver) { public Dialect buildDialect(Map configValues, DialectResolutionInfoSource resolutionInfoSource) throws HibernateException { final Object dialectReference = configValues.get( AvailableSettings.DIALECT ); if ( !isEmpty( dialectReference ) ) { - return constructDialect( dialectReference ); + return constructDialect( dialectReference, resolutionInfoSource ); } else { return determineDialect( resolutionInfoSource ); @@ -68,10 +70,32 @@ private boolean isEmpty(Object dialectReference) { return true; } - private Dialect constructDialect(Object dialectReference) { - final Dialect dialect; + private Dialect constructDialect(Object dialectReference, DialectResolutionInfoSource resolutionInfoSource) { try { - dialect = strategySelector.resolveStrategy( Dialect.class, dialectReference ); + Dialect dialect = strategySelector.resolveStrategy( + Dialect.class, + dialectReference, + (Dialect) null, + (dialectClass) -> { + try { + try { + if (resolutionInfoSource != null) { + return dialectClass.getConstructor(DialectResolutionInfo.class).newInstance( + resolutionInfoSource.getDialectResolutionInfo() + ); + } + } + catch (NoSuchMethodException nsme) {} + return dialectClass.newInstance(); + } + catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new StrategySelectionException( + String.format( "Could not instantiate named dialect class [%s]", dialectClass.getName() ), + e + ); + } + } + ); if ( dialect == null ) { throw new HibernateException( "Unable to construct requested dialect [" + dialectReference + "]" ); } @@ -100,7 +124,6 @@ private Dialect determineDialect(DialectResolutionInfoSource resolutionInfoSourc throw new HibernateException( "Unable to determine Dialect without JDBC metadata " + "(please set 'javax.persistence.jdbc.url', 'hibernate.connection.url', or 'hibernate.dialect')" - ); } @@ -109,9 +132,9 @@ private Dialect determineDialect(DialectResolutionInfoSource resolutionInfoSourc if ( dialect == null ) { throw new HibernateException( - "Unable to determine Dialect to use [name=" + info.getDatabaseName() + - ", majorVersion=" + info.getDatabaseMajorVersion() + - "]; user must register resolver or explicitly set 'hibernate.dialect'" + "Unable to determine Dialect for " + info.getDatabaseName() + " " + + info.getDatabaseMajorVersion() + "." + info.getDatabaseMinorVersion() + + " (please set 'hibernate.dialect' or register a Dialect resolver)" ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/ExtractedDatabaseMetaDataImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/ExtractedDatabaseMetaDataImpl.java index 36a74edeb4..56e3072bc6 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/ExtractedDatabaseMetaDataImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/ExtractedDatabaseMetaDataImpl.java @@ -9,7 +9,6 @@ import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; @@ -22,7 +21,6 @@ import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.env.spi.SQLStateType; import org.hibernate.engine.jdbc.spi.TypeInfo; -import org.hibernate.internal.util.StringHelper; import org.hibernate.tool.schema.extract.spi.SequenceInformation; /** @@ -201,8 +199,13 @@ public Builder apply(DatabaseMetaData databaseMetaData) throws SQLException { doesDataDefinitionCauseTransactionCommit = databaseMetaData.dataDefinitionCausesTransactionCommit(); extraKeywords = parseKeywords( databaseMetaData.getSQLKeywords() ); sqlStateType = SQLStateType.interpretReportedSQLStateType( databaseMetaData.getSQLStateType() ); - lobLocatorUpdateCopy = databaseMetaData.locatorsUpdateCopy(); - typeInfoSet = new LinkedHashSet(); + try { + lobLocatorUpdateCopy = databaseMetaData.locatorsUpdateCopy(); + } + catch (SQLException sql) { + //ignore, the database might not support this at all + } + typeInfoSet = new LinkedHashSet<>(); typeInfoSet.addAll( TypeInfo.extractTypeInfo( databaseMetaData ) ); return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentInitiator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentInitiator.java index 70c44e310f..4c359bea36 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentInitiator.java @@ -14,6 +14,7 @@ import org.hibernate.HibernateException; import org.hibernate.MultiTenancyStrategy; import org.hibernate.boot.registry.StandardServiceInitiator; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; @@ -62,7 +63,48 @@ public JdbcEnvironment initiateService(Map configurationValues, ServiceRegistryI true ); - if ( useJdbcMetadata ) { + if ( configurationValues.containsKey( AvailableSettings.HBM2DDL_DB_NAME ) ) { + return new JdbcEnvironmentImpl( registry, dialectFactory.buildDialect( + configurationValues, + () -> new DialectResolutionInfo() { + @Override + public String getDatabaseName() { + return (String) configurationValues.get( AvailableSettings.HBM2DDL_DB_NAME ); + } + + @Override + public String getDatabaseVersion() { + return (String) configurationValues.getOrDefault( AvailableSettings.HBM2DDL_DB_VERSION, "0" ); + } + + @Override + public int getDatabaseMajorVersion() { + return (Integer) configurationValues.getOrDefault( AvailableSettings.HBM2DDL_DB_MAJOR_VERSION, 0 ); + } + + @Override + public int getDatabaseMinorVersion() { + return (Integer) configurationValues.getOrDefault( AvailableSettings.HBM2DDL_DB_MINOR_VERSION, 0 ); + } + + @Override + public String getDriverName() { + throw new UnsupportedOperationException(); + } + + @Override + public int getDriverMajorVersion() { + throw new UnsupportedOperationException(); + } + + @Override + public int getDriverMinorVersion() { + throw new UnsupportedOperationException(); + } + } + ) ); + } + else if ( useJdbcMetadata ) { final JdbcConnectionAccess jdbcConnectionAccess = buildJdbcConnectionAccess( configurationValues, registry ); try { final Connection connection = jdbcConnectionAccess.obtainConnection(); @@ -96,26 +138,19 @@ public JdbcEnvironment initiateService(Map configurationValues, ServiceRegistryI Dialect dialect = dialectFactory.buildDialect( configurationValues, - new DialectResolutionInfoSource() { - @Override - public DialectResolutionInfo getDialectResolutionInfo() { - try { - return new DatabaseMetaDataDialectResolutionInfoAdapter( connection.getMetaData() ); - } - catch ( SQLException sqlException ) { - throw new HibernateException( - "Unable to access java.sql.DatabaseMetaData to determine appropriate Dialect to use", - sqlException - ); - } + () -> { + try { + return new DatabaseMetaDataDialectResolutionInfoAdapter( connection.getMetaData() ); + } + catch ( SQLException sqlException ) { + throw new HibernateException( + "Unable to access java.sql.DatabaseMetaData to determine appropriate Dialect to use", + sqlException + ); } } ); - return new JdbcEnvironmentImpl( - registry, - dialect, - dbmd - ); + return new JdbcEnvironmentImpl( registry, dialect, dbmd ); } catch (SQLException e) { log.unableToObtainConnectionMetadata( e.getMessage() ); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/log/ConnectionPoolingLogger.java b/hibernate-core/src/main/java/org/hibernate/internal/log/ConnectionPoolingLogger.java index a4ca7b8109..0fea326082 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/log/ConnectionPoolingLogger.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/log/ConnectionPoolingLogger.java @@ -9,8 +9,6 @@ import java.sql.SQLException; import java.util.Properties; -import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; - import org.jboss.logging.BasicLogger; import org.jboss.logging.Logger; import org.jboss.logging.annotations.Cause; @@ -48,12 +46,24 @@ public interface ConnectionPoolingLogger extends BasicLogger { @Message(value = "Autocommit mode: %s", id = 10001003) void autoCommitMode(boolean autocommit); - @Message(value = "JDBC URL was not specified by property %s", id = 10001004) - String jdbcUrlNotSpecified(String url); + @Message(value = "No JDBC URL specified by property %s", id = 10001004) + String jdbcUrlNotSpecified(String property); @LogMessage(level = INFO) - @Message(value = "using driver [%s] at URL [%s]", id = 10001005) - void usingDriver(String driverClassName, String url); + @Message(value = "Loaded JDBC driver class: %s", id = 10001005) + void loadedDriver(String driverClassName); + + @LogMessage(level = INFO) + @Message(value = "No JDBC driver class specified by %s", id = 10001010) + void noDriver(String property); + + @LogMessage(level = INFO) + @Message(value = "Loaded JDBC drivers: %s", id = 10001011) + void loadedDrivers(String loadedDrivers); + + @LogMessage(level = INFO) + @Message(value = "Connecting with JDBC URL [%s]", id = 10001012) + void usingUrl(String url); @LogMessage(level = WARN) @Message(value = "No JDBC Driver class was specified by property %s", id = 10001006)