Skip to content

Delegate detection of the database version to the Hibernate ORM dialect and enable checks for Hibernate Reactive #43764

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions docs/src/main/asciidoc/hibernate-orm.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -214,12 +214,6 @@ Hibernate ORM may generate SQL that is invalid which would lead to runtime excep
If the database cannot be reached, a warning will be logged but startup will proceed.
You can optionally disable the version check if you know the database won't be reachable on startup
using <<quarkus-hibernate-orm_quarkus-hibernate-orm-database-version-check-enabled,`quarkus.hibernate-orm.database.version-check.enabled=false`>>.

// TODO change the default to "always enabled" when we solve version detection problems
// See https://github.com/quarkusio/quarkus/issues/43703
// See https://github.com/quarkusio/quarkus/issues/42255
The version check is disabled by default when a dialect is set explicitly,
as a workaround for https://github.com/quarkusio/quarkus/issues/42255[#42255]/link:https://github.com/quarkusio/quarkus/issues/43703[#43703].
====

[[hibernate-dialect-other-databases]]
Expand Down
3 changes: 3 additions & 0 deletions docs/src/main/asciidoc/hibernate-reactive.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ A `Mutiny.SessionFactory` will be created based on the Quarkus `datasource` conf

The dialect will be selected based on the Reactive SQL client - unless you set one explicitly.

NOTE: For more information on dialect selection and database versions,
see xref:hibernate-orm.adoc#hibernate-dialect[the corresponding section of the Hibernate ORM guide].

You can then happily inject your `Mutiny.SessionFactory`:

[source,java]
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@
import io.quarkus.test.QuarkusUnitTest;

/**
* Tests that the workaround for https://github.com/quarkusio/quarkus/issues/43703 /
* Tests that DB version checks can be disabled explicitly.
* <p>
* This was originally introduced to work around problems with version checks,
* such as https://github.com/quarkusio/quarkus/issues/43703 /
* https://github.com/quarkusio/quarkus/issues/42255
* is effective.
*/
// TODO remove this test when change the default to "always enabled" when we solve version detection problems
// See https://github.com/quarkusio/quarkus/issues/43703
// See https://github.com/quarkusio/quarkus/issues/42255
public class DbVersionCheckDisabledExplicitlyTest {

private static final String ACTUAL_H2_VERSION = DialectVersions.Defaults.H2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,8 @@ public class DbVersionInvalidPersistenceXmlTest {
.doesNotStartWith(CONFIGURED_DB_VERSION);
}

private static final String ACTUAL_H2_VERSION_REPORTED;
private static final String CONFIGURED_DB_VERSION_REPORTED;
static {
// For some reason Hibernate ORM does not catch the actual micro version of H2 and default to 0; no big deal.
ACTUAL_H2_VERSION_REPORTED = ACTUAL_H2_VERSION.replaceAll("\\.[\\d]+$", ".0");
// For some reason Hibernate ORM infers a micro version of 0; no big deal.
CONFIGURED_DB_VERSION_REPORTED = CONFIGURED_DB_VERSION + ".0";
}
Expand All @@ -52,10 +49,10 @@ public class DbVersionInvalidPersistenceXmlTest {
.hasMessageContainingAll(
"Persistence unit 'templatePU' was configured to run with a database version"
+ " of at least '" + CONFIGURED_DB_VERSION_REPORTED + "', but the actual version is '"
+ ACTUAL_H2_VERSION_REPORTED + "'",
+ ACTUAL_H2_VERSION + "'",
"Consider upgrading your database",
"Alternatively, rebuild your application with 'jakarta.persistence.database-product-version="
+ ACTUAL_H2_VERSION_REPORTED + "'",
+ ACTUAL_H2_VERSION + "'",
"this may disable some features and/or impact performance negatively"));

@Inject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,8 @@ public class DbVersionInvalidTest {
.doesNotStartWith(CONFIGURED_DB_VERSION);
}

private static final String ACTUAL_H2_VERSION_REPORTED;
private static final String CONFIGURED_DB_VERSION_REPORTED;
static {
// For some reason Hibernate ORM does not catch the actual micro version of H2 and default to 0; no big deal.
ACTUAL_H2_VERSION_REPORTED = ACTUAL_H2_VERSION.replaceAll("\\.[\\d]+$", ".0");
// For some reason Hibernate ORM infers a micro version of 0; no big deal.
CONFIGURED_DB_VERSION_REPORTED = CONFIGURED_DB_VERSION + ".0";
}
Expand All @@ -45,10 +42,10 @@ public class DbVersionInvalidTest {
.hasMessageContainingAll(
"Persistence unit '<default>' was configured to run with a database version"
+ " of at least '" + CONFIGURED_DB_VERSION_REPORTED + "', but the actual version is '"
+ ACTUAL_H2_VERSION_REPORTED + "'",
+ ACTUAL_H2_VERSION + "'",
"Consider upgrading your database",
"Alternatively, rebuild your application with 'quarkus.datasource.db-version="
+ ACTUAL_H2_VERSION_REPORTED + "'",
+ ACTUAL_H2_VERSION + "'",
"this may disable some features and/or impact performance negatively",
"disable the check with 'quarkus.hibernate-orm.database.version-check.enabled=false'"));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package io.quarkus.hibernate.orm.offline;

import jakarta.inject.Inject;
import jakarta.transaction.Transactional;

import org.assertj.core.api.Assertions;
import org.hibernate.Session;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.hibernate.orm.MyEntity;
import io.quarkus.hibernate.orm.SmokeTestUtils;
import io.quarkus.test.QuarkusUnitTest;

/**
* If nothing requires DB access on startup, we expect startup to proceed even if the database is not reachable.
* <p>
* There will likely be warnings being logged, but the application will start.
*/
public class DbOfflineOnStartupTest {

@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClass(SmokeTestUtils.class)
.addClass(MyEntity.class))
.withConfigurationResource("application.properties")
// Disable schema management
.overrideConfigKey("quarkus.hibernate-orm.schema-management.strategy",
"none")
// Pick a DB URL that is offline
.overrideConfigKey("quarkus.datasource.jdbc.url",
"jdbc:h2:tcp://localhost:9999/~/sample");

@Inject
Session session;

@Test
@Transactional
public void smokeTest() {
// We expect startup to succeed, but any DB access would obviously fail
var entity = new MyEntity("someName");
Assertions.assertThatThrownBy(() -> {
session.persist(entity);
session.flush();
})
.hasMessageContaining("Connection refused");
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,12 @@ private RuntimeSettings buildRuntimeSettings(String persistenceUnitName, Recorde
}

// Allow detection of driver/database capabilities on runtime init (was disabled during static init)
runtimeSettingsBuilder.put("hibernate.boot.allow_jdbc_metadata_access", "true");
runtimeSettingsBuilder.put(AvailableSettings.ALLOW_METADATA_ON_BOOT, "true");
// Remove database version information, if any;
// it was necessary during static init to force creation of a dialect,
// but now the dialect is there, and we'll reuse it.
// Keeping this information would prevent us from getting the actual information from the database on start.
runtimeSettingsBuilder.put(AvailableSettings.JAKARTA_HBM2DDL_DB_VERSION, null);

if (!persistenceUnitConfig.unsupportedProperties().isEmpty()) {
log.warnf("Persistence-unit [%s] sets unsupported properties."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,10 @@ interface HibernateOrmConfigPersistenceUnitDatabase {
*
* @asciidoclet
*/
// TODO change the default to "always enabled" when we solve version detection problems
// See https://github.com/quarkusio/quarkus/issues/43703
// See https://github.com/quarkusio/quarkus/issues/42255
// TODO disable the check by default when offline startup is opted in
// See https://github.com/quarkusio/quarkus/issues/13522
@WithName("version-check.enabled")
@ConfigDocDefault("`true` if the dialect was set automatically by Quarkus, `false` if it was set explicitly")
@ConfigDocDefault("`true`")
Optional<Boolean> versionCheckEnabled();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ private MergedSettings mergeSettings(QuarkusPersistenceUnitDefinition puDefiniti

// Quarkus specific

cfg.put("hibernate.boot.allow_jdbc_metadata_access", "false");
cfg.put(AvailableSettings.ALLOW_METADATA_ON_BOOT, "false");

// Disallow CDI during metadata building in anticipation for https://github.com/quarkusio/quarkus/issues/40897
cfg.put(AvailableSettings.ALLOW_EXTENSIONS_IN_CDI, "false");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import static org.hibernate.internal.CoreLogging.messageLogger;

import java.sql.SQLException;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -99,16 +98,16 @@ public void checkActualDbVersion() {
}

private Optional<DatabaseVersion> retrieveDbVersion(DialectResolutionInfoSource resolutionInfoSource) {
var databaseMetadata = resolutionInfoSource == null ? null
: resolutionInfoSource.getDialectResolutionInfo().getDatabaseMetadata();
if (databaseMetadata == null) {
return Optional.empty();
}
try {
var resolutionInfo = resolutionInfoSource == null ? null
// This may throw an exception if the DB cannot be reached, in particular with Hibernate Reactive.
: resolutionInfoSource.getDialectResolutionInfo();
if (resolutionInfo == null) {
return Optional.empty();
}
triedToRetrieveDbVersion = true;
return Optional.of(DatabaseVersion.make(databaseMetadata.getDatabaseMajorVersion(),
databaseMetadata.getDatabaseMinorVersion()));
} catch (RuntimeException | SQLException e) {
return Optional.of(dialect.determineDatabaseVersion(resolutionInfo));
} catch (RuntimeException e) {
LOG.warnf(e, "Persistence unit %1$s: Could not retrieve the database version to check it is at least %2$s",
persistenceUnitName, buildTimeDbVersion);
return Optional.empty();
Expand Down
Loading
Loading