diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/JdbcSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/JdbcSettings.java index 8f0f628a9d76..2ae730b7e75a 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/JdbcSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/JdbcSettings.java @@ -490,6 +490,21 @@ public interface JdbcSettings extends C3p0Settings, ProxoolSettings, AgroalSetti */ String ALLOW_METADATA_ON_BOOT = "hibernate.boot.allow_jdbc_metadata_access"; + /** + * Whether failures to access the JDBC {@linkplain java.sql.DatabaseMetaData metadata} should be ignored and the + * bootstrapping process should continue. Hibernate then uses a default JDBC + * {@linkplain org.hibernate.engine.jdbc.env.spi.JdbcEnvironment environment}. + *

+ * Failures to access the metadata can result in unexpected runtime errors when accessing the database, since the + * default JDBC environment might not correctly represent the capabilities of the underlying database. + *

+ * This setting only takes effect when the {@link #ALLOW_METADATA_ON_BOOT} setting is activated. + * + * @settingDefault {@code true} + * @see #ALLOW_METADATA_ON_BOOT + * @since 6.6 + */ + String IGNORE_METADATA_ACCESS_FAILURE_ON_BOOT = "hibernate.boot.ignore_jdbc_metadata_access_failure"; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Deprecated Hibernate settings 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 7038b1aa9fc3..ecc9492c96b8 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 @@ -13,13 +13,13 @@ import java.util.Map; import java.util.StringTokenizer; +import org.hibernate.HibernateException; import org.hibernate.boot.registry.StandardServiceInitiator; import org.hibernate.cfg.JdbcSettings; import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.Dialect; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.jdbc.batch.spi.BatchBuilder; -import org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator; import org.hibernate.engine.jdbc.connections.internal.DatabaseConnectionInfoImpl; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.DatabaseConnectionInfo; @@ -321,7 +321,7 @@ private JdbcEnvironmentImpl getJdbcEnvironmentUsingJdbcMetadata( return temporaryJdbcSessionOwner.transactionCoordinator.createIsolationDelegate().delegateWork( new AbstractReturningWork<>() { @Override - public JdbcEnvironmentImpl execute(Connection connection) { + public JdbcEnvironmentImpl execute(Connection connection) throws SQLException { try { final DatabaseMetaData metadata = connection.getMetaData(); logDatabaseAndDriver( metadata ); @@ -357,7 +357,12 @@ public JdbcEnvironmentImpl execute(Connection connection) { ); } catch (SQLException e) { - log.unableToObtainConnectionMetadata( e ); + if ( shouldIgnoreMetadataAccessFailure( configurationValues ) ) { + log.unableToObtainConnectionMetadata( e ); + } + else { + throw e; + } } // accessing the JDBC metadata failed @@ -387,12 +392,25 @@ private int databaseMicroVersion(DatabaseMetaData metadata) throws SQLException ); } catch ( Exception e ) { - log.unableToObtainConnectionToQueryMetadata( e ); + if ( shouldIgnoreMetadataAccessFailure( configurationValues ) ) { + log.unableToObtainConnectionToQueryMetadata( e ); + } + else { + throw new HibernateException( "Unable to access JDBC metadata", e ); + } } // accessing the JDBC metadata failed return getJdbcEnvironmentWithDefaults( configurationValues, registry, dialectFactory ); } + private static boolean shouldIgnoreMetadataAccessFailure(Map configurationValues) { + return getBoolean( + JdbcSettings.IGNORE_METADATA_ACCESS_FAILURE_ON_BOOT, + configurationValues, + true + ); + } + private static void logDatabaseAndDriver(DatabaseMetaData dbmd) throws SQLException { if ( log.isDebugEnabled() ) { log.debugf( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/database/metadata/MetadataAccessTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/database/metadata/MetadataAccessTests.java index b5cd886cae2a..4b16ade67b52 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/database/metadata/MetadataAccessTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/database/metadata/MetadataAccessTests.java @@ -6,10 +6,6 @@ */ package org.hibernate.orm.test.boot.database.metadata; -import static org.assertj.core.api.Assertions.assertThat; -import static org.hibernate.dialect.SimpleDatabaseVersion.ZERO_VERSION; -import static org.junit.jupiter.api.Assertions.fail; - import java.lang.reflect.Field; import java.util.stream.Stream; @@ -50,6 +46,11 @@ import org.jboss.logging.Logger; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.hibernate.dialect.SimpleDatabaseVersion.ZERO_VERSION; +import static org.junit.jupiter.api.Assertions.fail; + /** * @author Steve Ebersole */ @@ -203,6 +204,23 @@ void testAccessDisabledNoDialectNorProductName() { } } + @Test + @Jira("https://hibernate.atlassian.net/browse/HHH-18286") + void testDontIgnoreMetadataAccessFailureWhenConnectionCantBeObtained() { + StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder(); + registryBuilder.clearSettings(); + + registryBuilder.applySetting( JdbcSettings.IGNORE_METADATA_ACCESS_FAILURE_ON_BOOT, false ); + + try (StandardServiceRegistry registry = registryBuilder.build()) { + assertThatExceptionOfType( ServiceException.class ) + .isThrownBy( () -> registry.getService( JdbcEnvironment.class ) ) + .havingCause() + .isInstanceOf( HibernateException.class ) + .withMessage( "Unable to access JDBC metadata" ); + } + } + // Ugly hack because neither MINIMUM_VERSION nor getMinimumSupportedVersion() // can be accessed from this test. private static DatabaseVersion getVersionConstant(Class dialectClass, String versionConstantName) {