From 031ce835488551d2bddde08447058e7fd325cb6f Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 4 Sep 2025 14:45:37 +0200 Subject: [PATCH 1/2] HHH-19752 Add test for issue --- .../unit/lockhint/MySQLStorageEngineTest.java | 78 ++++++++++++++++--- 1 file changed, 68 insertions(+), 10 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/MySQLStorageEngineTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/MySQLStorageEngineTest.java index f1a3da96e901..12907d59f07f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/MySQLStorageEngineTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/MySQLStorageEngineTest.java @@ -4,26 +4,37 @@ */ package org.hibernate.orm.test.dialect.unit.lockhint; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.Dialect; import org.hibernate.dialect.MySQLDialect; - -import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.hibernate.orm.test.dialect.resolver.TestingDialectResolutionInfo; +import org.hibernate.testing.orm.junit.BaseUnitTest; +import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.RequiresDialect; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.Test; import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; import java.util.Properties; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + @RequiresDialect(MySQLDialect.class) -public class MySQLStorageEngineTest extends BaseUnitTestCase { +@BaseUnitTest +public class MySQLStorageEngineTest { @Test public void testDefaultStorage() { - assertEquals( " engine=InnoDB", new MySQLDialect().getTableTypeString() ); + assertThat( new MySQLDialect().getTableTypeString() ).isEqualTo( " engine=InnoDB" ); } @Test @@ -31,10 +42,49 @@ public void testOverrideStorage() throws NoSuchFieldException, IllegalAccessExce final Field globalPropertiesField = Environment.class.getDeclaredField( "GLOBAL_PROPERTIES" ); globalPropertiesField.setAccessible( true ); final Properties systemProperties = (Properties) globalPropertiesField.get( null ); - assertNotNull( systemProperties ); + assertThat( systemProperties ).isNotNull(); + final Object previousValue = systemProperties.setProperty( AvailableSettings.STORAGE_ENGINE, "myisam" ); + try { + assertThat( new MySQLDialect().getTableTypeString() ).isEqualTo( " engine=MyISAM" ); + } + finally { + if ( previousValue != null ) { + systemProperties.setProperty( AvailableSettings.STORAGE_ENGINE, previousValue.toString() ); + } + else { + systemProperties.remove( AvailableSettings.STORAGE_ENGINE ); + } + } + } + + @Test + @SessionFactory + @ServiceRegistry(settings = {@Setting(name = AvailableSettings.STORAGE_ENGINE, value = "myisam")}) + @DomainModel(annotatedClasses = {TestEntity.class}) + public void testOverrideStorageWithConfigurationProperties(SessionFactoryScope scope) { + Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect(); + assertThat( dialect.getTableTypeString() ).isEqualTo( " engine=MyISAM" ); + } + + @Test + public void testOverrideStorageEngineConfigurationPropertyHasPrecedenceOverSystemProperty() + throws Exception { + final Field globalPropertiesField = Environment.class.getDeclaredField( "GLOBAL_PROPERTIES" ); + globalPropertiesField.setAccessible( true ); + final Properties systemProperties = (Properties) globalPropertiesField.get( null ); + assertThat( systemProperties ).isNotNull(); final Object previousValue = systemProperties.setProperty( AvailableSettings.STORAGE_ENGINE, "myisam" ); try { - assertEquals( " engine=MyISAM", new MySQLDialect().getTableTypeString() ); + final Map configurationValues = new HashMap<>(); + configurationValues.put( AvailableSettings.STORAGE_ENGINE, "innodb" ); + Dialect dialect = new MySQLDialect( + TestingDialectResolutionInfo.forDatabaseInfo( + "MySQL", + null, + DatabaseVersion.NO_VERSION, + DatabaseVersion.NO_VERSION, + configurationValues ) ); + assertThat( dialect.getTableTypeString() ).isEqualTo( " engine=InnoDB" ); } finally { if ( previousValue != null ) { @@ -46,4 +96,12 @@ public void testOverrideStorage() throws NoSuchFieldException, IllegalAccessExce } } + @Entity(name = "TestEntity") + public static class TestEntity{ + @Id + private Long id; + + private String name; + } + } From 30115fc51509dcbce30538b3a6a3d286565663e4 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 4 Sep 2025 12:06:02 +0200 Subject: [PATCH 2/2] HHH-19752 Allow setting MariaDB/MySQL storage engine not only using System properties but also configuration properties --- .../hibernate/cfg/SchemaToolingSettings.java | 7 ++++--- .../org/hibernate/dialect/MySQLDialect.java | 19 ++++++++++-------- .../dialect/MySQLServerConfiguration.java | 20 ++++++++++++++++++- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/SchemaToolingSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/SchemaToolingSettings.java index b8becd4334ff..ffff3b0525d3 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/SchemaToolingSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/SchemaToolingSettings.java @@ -295,9 +295,10 @@ public interface SchemaToolingSettings { /** * Specifies the default storage engine for a relational database that supports - * multiple storage engines. This property must be set either as an {@link Environment} - * variable or JVM System Property, since the {@link org.hibernate.dialect.Dialect} is - * instantiated before Hibernate property resolution. + * multiple storage engines. + * + * This property can be set as an {@link Environment} variable, a JVM System Property + * or a configuration property. *

* For MySQL, the legal values are {@code innodb} (the default) and {@code myisam}. * diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java index 3c64dec330f4..42d47073987d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -93,6 +93,7 @@ import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; +import java.util.Locale; import java.util.TimeZone; import static java.lang.Integer.parseInt; @@ -146,7 +147,7 @@ public class MySQLDialect extends Dialect { private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 8 ); - private final MySQLStorageEngine storageEngine = createStorageEngine(); + private final MySQLStorageEngine storageEngine; private final SizeStrategy sizeStrategy = new SizeStrategyImpl() { @Override @@ -207,7 +208,11 @@ public MySQLDialect(DatabaseVersion version, int bytesPerCharacter) { } public MySQLDialect(DatabaseVersion version, MySQLServerConfiguration serverConfiguration) { - this( version, serverConfiguration.getBytesPerCharacter(), serverConfiguration.isNoBackslashEscapesEnabled() ); + super( version ); + maxVarcharLength = maxVarcharLength( getMySQLVersion(), serverConfiguration.getBytesPerCharacter() ); //conservative assumption + maxVarbinaryLength = maxVarbinaryLength( getMySQLVersion() ); + noBackslashEscapesEnabled = serverConfiguration.isNoBackslashEscapesEnabled(); + storageEngine = createStorageEngine( serverConfiguration.getConfiguredStorageEngine() ); } public MySQLDialect(DatabaseVersion version, int bytesPerCharacter, boolean noBackslashEscapes) { @@ -215,6 +220,7 @@ public MySQLDialect(DatabaseVersion version, int bytesPerCharacter, boolean noBa maxVarcharLength = maxVarcharLength( getMySQLVersion(), bytesPerCharacter ); //conservative assumption maxVarbinaryLength = maxVarbinaryLength( getMySQLVersion() ); noBackslashEscapesEnabled = noBackslashEscapes; + storageEngine = createStorageEngine( Environment.getProperties().getProperty( AvailableSettings.STORAGE_ENGINE ) ); } public MySQLDialect(DialectResolutionInfo info) { @@ -257,13 +263,10 @@ protected void initDefaultProperties() { getDefaultProperties().setProperty( FetchSettings.MAX_FETCH_DEPTH, "2" ); } - private MySQLStorageEngine createStorageEngine() { - final String storageEngine = - Environment.getProperties() - .getProperty( AvailableSettings.STORAGE_ENGINE ); - return storageEngine == null + private MySQLStorageEngine createStorageEngine(String configuredStorageEngine) { + return configuredStorageEngine == null ? getDefaultMySQLStorageEngine() - : switch ( storageEngine ) { + : switch ( configuredStorageEngine.toLowerCase(Locale.ROOT) ) { case "innodb" -> InnoDBStorageEngine.INSTANCE; case "myisam" -> MyISAMStorageEngine.INSTANCE; default -> throw new UnsupportedOperationException( diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLServerConfiguration.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLServerConfiguration.java index 362f7af9c668..bb8905e2488f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLServerConfiguration.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLServerConfiguration.java @@ -9,7 +9,9 @@ import java.sql.SQLException; import org.hibernate.Internal; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; +import org.hibernate.internal.util.config.ConfigurationHelper; import static org.hibernate.cfg.DialectSpecificSettings.MYSQL_BYTES_PER_CHARACTER; import static org.hibernate.cfg.DialectSpecificSettings.MYSQL_NO_BACKSLASH_ESCAPES; @@ -26,10 +28,18 @@ public class MySQLServerConfiguration { private final int bytesPerCharacter; private final boolean noBackslashEscapesEnabled; + private final String storageEngine; public MySQLServerConfiguration(int bytesPerCharacter, boolean noBackslashEscapesEnabled) { this.bytesPerCharacter = bytesPerCharacter; this.noBackslashEscapesEnabled = noBackslashEscapesEnabled; + this.storageEngine = null; + } + + public MySQLServerConfiguration(int bytesPerCharacter, boolean noBackslashEscapesEnabled, String storageEngine) { + this.bytesPerCharacter = bytesPerCharacter; + this.noBackslashEscapesEnabled = noBackslashEscapesEnabled; + this.storageEngine = storageEngine; } public int getBytesPerCharacter() { @@ -40,6 +50,10 @@ public boolean isNoBackslashEscapesEnabled() { return noBackslashEscapesEnabled; } + public String getConfiguredStorageEngine() { + return storageEngine; + } + static Integer getBytesPerCharacter(String characterSet) { final int collationIndex = characterSet.indexOf( '_' ); // According to https://dev.mysql.com/doc/refman/8.0/en/charset-charsets.html @@ -78,7 +92,11 @@ public static MySQLServerConfiguration fromDialectResolutionInfo(DialectResoluti if ( noBackslashEscapes == null ) { noBackslashEscapes = getBoolean( MYSQL_NO_BACKSLASH_ESCAPES, info.getConfigurationValues(), false ); } - return new MySQLServerConfiguration( bytesPerCharacter, noBackslashEscapes ); + + return new MySQLServerConfiguration( + bytesPerCharacter, + noBackslashEscapes, + ConfigurationHelper.getString( AvailableSettings.STORAGE_ENGINE, info.getConfigurationValues() ) ); } /**