This is an automated email from the ASF dual-hosted git repository. borinquenkid pushed a commit to branch merge-hibernate6 in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 0e11f81a1787e62a121890acb91dd70023ac4796 Author: Walter Duque de Estrada <[email protected]> AuthorDate: Wed Jul 9 20:20:00 2025 -0500 refactoring and testing NamingStrategyProvider --- .../orm/hibernate/cfg/GrailsDomainBinder.java | 78 ++++++-------- .../cfg/domainbinding/NamingStrategyProvider.java | 75 +++++++++++++ .../NamingStrategyProviderSpec.groovy | 116 +++++++++++++++++++++ 3 files changed, 221 insertions(+), 48 deletions(-) diff --git a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java index 14084c1902..8a61ddb898 100644 --- a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java +++ b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java @@ -36,12 +36,12 @@ import org.grails.datastore.mapping.reflect.NameUtils; import org.grails.orm.hibernate.access.TraitPropertyAccessStrategy; import org.grails.orm.hibernate.cfg.domainbinding.ClassBinder; import org.grails.orm.hibernate.cfg.domainbinding.ConfigureDerivedPropertiesConsumer; +import org.grails.orm.hibernate.cfg.domainbinding.NamingStrategyProvider; import org.grails.orm.hibernate.cfg.domainbinding.TypeNameProvider; import org.hibernate.FetchMode; import org.hibernate.MappingException; import org.hibernate.boot.internal.MetadataBuildingContextRootImpl; import org.hibernate.boot.model.internal.BinderHelper; -import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy; import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.PhysicalNamingStrategy; import org.hibernate.boot.spi.AccessType; @@ -144,14 +144,13 @@ public class GrailsDomainBinder implements MetadataContributor { private static final String DEFAULT_ENUM_TYPE = "default"; private static final Logger LOG = LoggerFactory.getLogger(GrailsDomainBinder.class); public static final String SEQUENCE_KEY = "sequence"; + + + /** - * Overrideable naming strategy. Defaults to <code>ImprovedNamingStrategy</code> but can - * be configured in DataSource.groovy via <code>hibernate.naming_strategy = ...</code>. + * Provider for naming strategies */ - public static Map<String, PhysicalNamingStrategy> NAMING_STRATEGIES = new HashMap<>(); - static { - NAMING_STRATEGIES.put(ConnectionSource.DEFAULT, new CamelCaseToUnderscoresNamingStrategy()); - } + private static final NamingStrategyProvider NAMING_STRATEGY_PROVIDER = new NamingStrategyProvider(); private final CollectionType CT = new CollectionType(null, this) { public Collection create(ToMany property, PersistentClass owner, String path, InFlightMetadataCollector mappings, String sessionFactoryBeanName) { @@ -167,6 +166,7 @@ public class GrailsDomainBinder implements MetadataContributor { private PersistentEntityNamingStrategy namingStrategy; private MetadataBuildingContext metadataBuildingContext; + public JdbcEnvironment getJdbcEnvironment() { return metadataBuildingContext.getMetadataCollector().getDatabase().getJdbcEnvironment(); } @@ -200,6 +200,7 @@ public class GrailsDomainBinder implements MetadataContributor { } + /** * The default mapping defined by {@link org.grails.datastore.mapping.config.Settings#SETTING_DEFAULT_MAPPING} * @param defaultMapping The default mapping @@ -269,23 +270,7 @@ public class GrailsDomainBinder implements MetadataContributor { * @throws IllegalAccessException When an error occurred instantiating the strategy */ public static void configureNamingStrategy(final String datasourceName, final Object strategy) throws ClassNotFoundException, InstantiationException, IllegalAccessException { - Class<?> namingStrategyClass = null; - PhysicalNamingStrategy namingStrategy; - if (strategy instanceof Class<?>) { - namingStrategyClass = (Class<?>)strategy; - } - else if (strategy instanceof CharSequence) { - namingStrategyClass = Thread.currentThread().getContextClassLoader().loadClass(strategy.toString()); - } - - if (namingStrategyClass == null) { - namingStrategy = (PhysicalNamingStrategy)strategy; - } - else { - namingStrategy = (PhysicalNamingStrategy)namingStrategyClass.newInstance(); - } - - NAMING_STRATEGIES.put(datasourceName, namingStrategy); + NAMING_STRATEGY_PROVIDER.configureNamingStrategy(datasourceName, strategy); } private void bindMapSecondPass(ToMany property, InFlightMetadataCollector mappings, @@ -732,7 +717,7 @@ public class GrailsDomainBinder implements MetadataContributor { private void bindCollectionWithJoinTable(ToMany property, InFlightMetadataCollector mappings, Collection collection, PropertyConfig config, String sessionFactoryBeanName) { - PhysicalNamingStrategy namingStrategy = getNamingStrategy(sessionFactoryBeanName); + PhysicalNamingStrategy namingStrategy = getPhysicalNamingStrategy(sessionFactoryBeanName); SimpleValue element; final boolean isBasicCollectionType = property instanceof Basic; @@ -757,11 +742,9 @@ public class GrailsDomainBinder implements MetadataContributor { columnName = config.getJoinTable().getColumn().getName(); } else { - - - var s = namingStrategy.toPhysicalColumnName(toIdentifier(className),getJdbcEnvironment()); - var p = namingStrategy.toPhysicalColumnName(toIdentifier(property.getName()),getJdbcEnvironment()); - columnName = isEnum ? s.toString() : addUnderscore(p.toString(), s.toString()); + var clazz = namingStrategy.toPhysicalColumnName(toIdentifier(className),getJdbcEnvironment()); + var prop = namingStrategy.toPhysicalTableName(toIdentifier(property.getName()),getJdbcEnvironment()); + columnName = isEnum ? clazz.toString() : addUnderscore(prop.toString(), clazz.toString()); } if (isEnum) { @@ -1174,7 +1157,7 @@ public class GrailsDomainBinder implements MetadataContributor { PropertyConfig config = getPropertyConfig(property); JoinTable jt = config != null ? config.getJoinTable() : null; - PhysicalNamingStrategy namingStrategy = getNamingStrategy(sessionFactoryBeanName); + PhysicalNamingStrategy namingStrategy = getPhysicalNamingStrategy(sessionFactoryBeanName); String s = calculateTableForMany(property, sessionFactoryBeanName); String tableName = (jt != null && jt.getName() != null ? jt.getName() : namingStrategy.toPhysicalTableName(toIdentifier(s),getJdbcEnvironment()).toString()); String schemaName = getSchemaName(mappings); @@ -1203,7 +1186,7 @@ public class GrailsDomainBinder implements MetadataContributor { * where you have two mapping tables for left_right and right_left */ private String calculateTableForMany(ToMany property, String sessionFactoryBeanName) { - PhysicalNamingStrategy namingStrategy = getNamingStrategy(sessionFactoryBeanName); + PhysicalNamingStrategy namingStrategy = getPhysicalNamingStrategy(sessionFactoryBeanName); String propertyColumnName = namingStrategy.toPhysicalColumnName(toIdentifier(property.getName()), getJdbcEnvironment()).toString(); //fix for GRAILS-5895 @@ -1285,20 +1268,18 @@ public class GrailsDomainBinder implements MetadataContributor { tableName = namingStrategy.resolveTableName(domainClass); } if(tableName == null) { - tableName = getNamingStrategy(sessionFactoryBeanName).toPhysicalTableName(toIdentifier(shortName),getJdbcEnvironment()).toString(); + tableName = getPhysicalNamingStrategy(sessionFactoryBeanName).toPhysicalTableName(toIdentifier(shortName),getJdbcEnvironment()).toString(); } } return tableName; } - private PhysicalNamingStrategy getNamingStrategy(String sessionFactoryBeanName) { - String key = "sessionFactory".equals(sessionFactoryBeanName) ? - ConnectionSource.DEFAULT : - sessionFactoryBeanName.substring("sessionFactory_".length()); - PhysicalNamingStrategy namingStrategy = NAMING_STRATEGIES.get(key); - return namingStrategy != null ? namingStrategy : new CamelCaseToUnderscoresNamingStrategy(); + private PhysicalNamingStrategy getPhysicalNamingStrategy(String sessionFactoryBeanName) { + return NAMING_STRATEGY_PROVIDER.getPhysicalNamingStrategy(sessionFactoryBeanName); } + + /** * Binds a Grails domain class to the Hibernate runtime meta model * @@ -1377,7 +1358,7 @@ public class GrailsDomainBinder implements MetadataContributor { * @return A Mapping object or null */ private static Mapping getMapping(PersistentEntity domainClass) { - return domainClass == null ? null : AbstractGrailsDomainBinder.getMapping(domainClass.getJavaClass()); + return getMapping(domainClass.getJavaClass()); } public static void clearMappingCache() { @@ -1770,7 +1751,7 @@ public class GrailsDomainBinder implements MetadataContributor { var isAbstract = !m.getTablePerHierarchy() && m.isTablePerConcreteClass() && root.isAbstract(); // create the table - Table table = mappings.addTable(schema + var table = mappings.addTable(schema , catalog , getTableName(domainClass, sessionFactoryBeanName) , null @@ -2325,7 +2306,8 @@ public class GrailsDomainBinder implements MetadataContributor { private void bindManyToOne(Association property, ManyToOne manyToOne, String path, InFlightMetadataCollector mappings, String sessionFactoryBeanName) { - PhysicalNamingStrategy namingStrategy = getNamingStrategy(sessionFactoryBeanName); + PhysicalNamingStrategy namingStrategy = getPhysicalNamingStrategy(sessionFactoryBeanName); + bindManyToOneValues(property, manyToOne); PersistentEntity refDomainClass = property instanceof ManyToMany ? property.getOwner() : property.getAssociatedEntity(); @@ -2374,7 +2356,7 @@ public class GrailsDomainBinder implements MetadataContributor { SimpleValue value, CompositeIdentity compositeId, PersistentEntity refDomainClass, String path, String sessionFactoryBeanName) { - PhysicalNamingStrategy namingStrategy = getNamingStrategy(sessionFactoryBeanName); + PhysicalNamingStrategy namingStrategy = getPhysicalNamingStrategy(sessionFactoryBeanName); String[] propertyNames = compositeId.getPropertyNames(); PropertyConfig config = getPropertyConfig(property); @@ -3095,7 +3077,7 @@ public class GrailsDomainBinder implements MetadataContributor { private String getColumnNameForPropertyAndPath(PersistentProperty grailsProp, String path, ColumnConfig cc, String sessionFactoryBeanName) { - PhysicalNamingStrategy namingStrategy = getNamingStrategy(sessionFactoryBeanName); + PhysicalNamingStrategy namingStrategy = getPhysicalNamingStrategy(sessionFactoryBeanName); // First try the column config. String columnName = null; @@ -3150,7 +3132,7 @@ public class GrailsDomainBinder implements MetadataContributor { private String getDefaultColumnName(PersistentProperty property, String sessionFactoryBeanName) { - PhysicalNamingStrategy namingStrategy = getNamingStrategy(sessionFactoryBeanName); + PhysicalNamingStrategy namingStrategy = getPhysicalNamingStrategy(sessionFactoryBeanName); String columnName = namingStrategy.toPhysicalColumnName(toIdentifier(property.getName()), getJdbcEnvironment()).toString(); if (property instanceof Association) { @@ -3187,7 +3169,7 @@ public class GrailsDomainBinder implements MetadataContributor { private String getForeignKeyForPropertyDomainClass(PersistentProperty property, String sessionFactoryBeanName) { final String propertyName = NameUtils.decapitalize( property.getOwner().getName() ); - PhysicalNamingStrategy namingStrategy = getNamingStrategy(sessionFactoryBeanName); + PhysicalNamingStrategy namingStrategy = getPhysicalNamingStrategy(sessionFactoryBeanName); return namingStrategy.toPhysicalColumnName(toIdentifier(propertyName), getJdbcEnvironment()).toString() + FOREIGN_KEY_SUFFIX; } @@ -3196,7 +3178,7 @@ public class GrailsDomainBinder implements MetadataContributor { if (pc != null && pc.getIndexColumn() != null && pc.getIndexColumn().getColumn() != null) { return pc.getIndexColumn().getColumn(); } - PhysicalNamingStrategy namingStrategy = getNamingStrategy(sessionFactoryBeanName); + PhysicalNamingStrategy namingStrategy = getPhysicalNamingStrategy(sessionFactoryBeanName); return namingStrategy.toPhysicalColumnName(toIdentifier(property.getName()), getJdbcEnvironment()).toString() + UNDERSCORE + IndexedCollection.DEFAULT_INDEX_COLUMN_NAME; } @@ -3217,7 +3199,7 @@ public class GrailsDomainBinder implements MetadataContributor { return pc.getJoinTable().getColumn().getName(); } - PhysicalNamingStrategy namingStrategy = getNamingStrategy(sessionFactoryBeanName); + PhysicalNamingStrategy namingStrategy = getPhysicalNamingStrategy(sessionFactoryBeanName); return namingStrategy.toPhysicalColumnName(toIdentifier(property.getName()), getJdbcEnvironment()).toString() + UNDERSCORE + IndexedCollection.DEFAULT_ELEMENT_COLUMN_NAME; } diff --git a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/NamingStrategyProvider.java b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/NamingStrategyProvider.java new file mode 100644 index 0000000000..05821b9e79 --- /dev/null +++ b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/NamingStrategyProvider.java @@ -0,0 +1,75 @@ +package org.grails.orm.hibernate.cfg.domainbinding; + +import org.grails.datastore.mapping.core.connections.ConnectionSource; +import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy; +import org.hibernate.boot.model.naming.PhysicalNamingStrategy; + +import java.util.concurrent.ConcurrentHashMap; + +public class NamingStrategyProvider { + private final ConcurrentHashMap<String, PhysicalNamingStrategy> physicalProviderMap; + + public NamingStrategyProvider() { + physicalProviderMap = new ConcurrentHashMap<>(); + physicalProviderMap.put(ConnectionSource.DEFAULT, new CamelCaseToUnderscoresNamingStrategy()); + } + + /** + * Configures the naming strategy for a given datasource. + * + * @param datasourceName the datasource name + * @param strategy the naming strategy (instance, Class, or class name) + * @throws ClassNotFoundException when the strategy class cannot be found + * @throws IllegalAccessException when the strategy class cannot be accessed + * @throws InstantiationException when the strategy class cannot be instantiated + */ + public void configureNamingStrategy(final String datasourceName, final Object strategy) + throws ClassNotFoundException, InstantiationException, IllegalAccessException { + + if (strategy == null) { + throw new IllegalArgumentException("Naming strategy cannot be null"); + } + + var strategyClass = getStrategyClass(strategy); + var strategyInstance = getStrategyInstance(strategy, strategyClass); + + if (strategyInstance instanceof PhysicalNamingStrategy physicalStrategy) { + physicalProviderMap.put(datasourceName, physicalStrategy); + } else { + physicalProviderMap.put(datasourceName, new CamelCaseToUnderscoresNamingStrategy()); + } + } + + private Class<?> getStrategyClass(Object strategy) throws ClassNotFoundException { + if (strategy instanceof Class<?>) { + return (Class<?>) strategy; + } + if (strategy instanceof CharSequence) { + return Thread.currentThread().getContextClassLoader().loadClass(strategy.toString()); + } + return strategy.getClass(); + } + + private Object getStrategyInstance(Object strategy, Class<?> strategyClass) + throws InstantiationException, IllegalAccessException { + if (strategy instanceof PhysicalNamingStrategy) { + return strategy; + } + return strategyClass.newInstance(); + } + + public PhysicalNamingStrategy getPhysicalNamingStrategy(String sessionFactoryBeanName) { + String key = getKey(sessionFactoryBeanName); + physicalProviderMap.putIfAbsent(key, new CamelCaseToUnderscoresNamingStrategy()); + return physicalProviderMap.get(key); + } + + + private static String getKey(String sessionFactoryBeanName) { + String key = "sessionFactory".equals(sessionFactoryBeanName) ? + ConnectionSource.DEFAULT : + sessionFactoryBeanName.substring("sessionFactory_".length()); + return key; + } + +} diff --git a/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/NamingStrategyProviderSpec.groovy b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/NamingStrategyProviderSpec.groovy new file mode 100644 index 0000000000..ac4f1260c8 --- /dev/null +++ b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/NamingStrategyProviderSpec.groovy @@ -0,0 +1,116 @@ +package org.grails.orm.hibernate.cfg.domainbinding + +import grails.gorm.specs.HibernateGormDatastoreSpec +import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy +import org.hibernate.boot.model.naming.PhysicalNamingStrategy + +class NamingStrategyProviderSpec extends HibernateGormDatastoreSpec { + + void "Test constructor initializes with default strategy"() { + when: + def provider = new NamingStrategyProvider() + def strategy = provider.getPhysicalNamingStrategy("sessionFactory") + + then: + strategy instanceof CamelCaseToUnderscoresNamingStrategy + } + + void "Test configureNamingStrategy with null strategy throws exception"() { + given: + def provider = new NamingStrategyProvider() + + when: + provider.configureNamingStrategy("test", null) + + then: + thrown(IllegalArgumentException) + } + + void "Test configureNamingStrategy with PhysicalNamingStrategy instance"() { + given: + def provider = new NamingStrategyProvider() + def mockStrategy = new MockPhysicalNamingStrategy() + + when: + provider.configureNamingStrategy("test", mockStrategy) + def strategy = provider.getPhysicalNamingStrategy("sessionFactory_test") + + then: + strategy instanceof MockPhysicalNamingStrategy + } + + void "Test configureNamingStrategy with Class"() { + given: + def provider = new NamingStrategyProvider() + + when: + provider.configureNamingStrategy("test", MockPhysicalNamingStrategy) + def strategy = provider.getPhysicalNamingStrategy("sessionFactory_test") + + then: + strategy instanceof MockPhysicalNamingStrategy + } + + void "Test configureNamingStrategy with class name"() { + given: + def provider = new NamingStrategyProvider() + + when: + provider.configureNamingStrategy("test", MockPhysicalNamingStrategy.name) + def strategy = provider.getPhysicalNamingStrategy("sessionFactory_test") + + then: + strategy instanceof MockPhysicalNamingStrategy + } + + void "Test getPhysicalNamingStrategy with default session factory"() { + given: + def provider = new NamingStrategyProvider() + + when: + def strategy = provider.getPhysicalNamingStrategy("sessionFactory") + + then: + strategy instanceof CamelCaseToUnderscoresNamingStrategy + } + + void "Test getPhysicalNamingStrategy with custom session factory"() { + given: + def provider = new NamingStrategyProvider() + def mockStrategy = new MockPhysicalNamingStrategy() + provider.configureNamingStrategy("custom", mockStrategy) + + when: + def strategy = provider.getPhysicalNamingStrategy("sessionFactory_custom") + + then: + strategy instanceof MockPhysicalNamingStrategy + } +} + +class MockPhysicalNamingStrategy implements PhysicalNamingStrategy { + @Override + org.hibernate.boot.model.naming.Identifier toPhysicalCatalogName(org.hibernate.boot.model.naming.Identifier name, org.hibernate.engine.jdbc.env.spi.JdbcEnvironment jdbcEnvironment) { + return name + } + + @Override + org.hibernate.boot.model.naming.Identifier toPhysicalSchemaName(org.hibernate.boot.model.naming.Identifier name, org.hibernate.engine.jdbc.env.spi.JdbcEnvironment jdbcEnvironment) { + return name + } + + @Override + org.hibernate.boot.model.naming.Identifier toPhysicalTableName(org.hibernate.boot.model.naming.Identifier name, org.hibernate.engine.jdbc.env.spi.JdbcEnvironment jdbcEnvironment) { + return name + } + + @Override + org.hibernate.boot.model.naming.Identifier toPhysicalSequenceName(org.hibernate.boot.model.naming.Identifier name, org.hibernate.engine.jdbc.env.spi.JdbcEnvironment jdbcEnvironment) { + return name + } + + @Override + org.hibernate.boot.model.naming.Identifier toPhysicalColumnName(org.hibernate.boot.model.naming.Identifier name, org.hibernate.engine.jdbc.env.spi.JdbcEnvironment jdbcEnvironment) { + return name + } +}
