This is an automated email from the ASF dual-hosted git repository. borinquenkid pushed a commit to branch 8.0.x-hibernate7 in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 7036d2ccf4fbf23eeb46140a9ac5ae0ef4695305 Author: Walter Duque de Estrada <[email protected]> AuthorDate: Sun Feb 8 20:48:50 2026 -0600 fixed OneToManySpec --- .../cfg/GrailsHibernatePersistentProperty.java | 3 ++ .../cfg/domainbinding/CascadeBehaviorFetcher.java | 2 +- .../cfg/domainbinding/CollectionBinder.java | 8 +++- .../hibernate/cfg/domainbinding/ColumnBinder.java | 2 +- .../domainbinding/DefaultColumnNameFetcher.java | 4 +- .../cfg/domainbinding/NamingStrategyWrapper.java | 4 +- .../cfg/domainbinding/SimpleValueBinder.java | 24 ++++++++-- .../cfg/domainbinding/TableNameFetcher.java | 5 ++ .../secondpass/CollectionSecondPassBinder.java | 54 +++++++++++++++++----- .../domainbinding/NamingStrategyWrapperSpec.groovy | 13 +++--- .../cfg/domainbinding/TableNameFetcherSpec.groovy | 9 ++-- .../config/GormMappingConfigurationStrategy.java | 7 +-- 12 files changed, 95 insertions(+), 40 deletions(-) diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentProperty.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentProperty.java index 7430a6c596..9302793678 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentProperty.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentProperty.java @@ -28,6 +28,9 @@ public interface GrailsHibernatePersistentProperty extends PersistentProperty<Pr * @return The type name */ default String getTypeName(PropertyConfig config, Mapping mapping) { + if (this instanceof Association) { + return null; + } return Optional.ofNullable(config) .map(PropertyConfig::getType) .map(typeObj -> typeObj instanceof Class<?> clazz ? clazz.getName() : typeObj.toString()) diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorFetcher.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorFetcher.java index 7527076913..69656ed08f 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorFetcher.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CascadeBehaviorFetcher.java @@ -59,7 +59,7 @@ public class CascadeBehaviorFetcher { return association.isOwningSide() ? ALL : SAVE_UPDATE; } else if (association.isOneToMany()) { - return association.isOwningSide() ? ALL : SAVE_UPDATE; + return association.isCorrectlyOwned() ? ALL : SAVE_UPDATE; } else if (association.isManyToMany()) { return association.isCorrectlyOwned() || association.isCircular() ? SAVE_UPDATE :NONE; } diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CollectionBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CollectionBinder.java index 8e9a362249..2a9b4ed21a 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CollectionBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/CollectionBinder.java @@ -93,8 +93,12 @@ public class CollectionBinder { } else { bindCollectionTable(property, mappings, collection, owner.getTable()); - if (!property.isOwningSide()) { - collection.setInverse(true); + if (property.isBidirectional()) { + if (!property.isOwningSide()) { + collection.setInverse(true); + } + } else { + collection.setInverse(false); } } diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/ColumnBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/ColumnBinder.java index 94a4a4cf29..85f7a25d1b 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/ColumnBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/ColumnBinder.java @@ -110,7 +110,7 @@ public class ColumnBinder { } else if ((property instanceof ToOne) && association.isCircular()) { column.setNullable(true); } else { - column.setNullable(property.isNullable()); + column.setNullable(true); } } else { column.setName(columnName); diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/DefaultColumnNameFetcher.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/DefaultColumnNameFetcher.java index 121b43ada9..d06338f362 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/DefaultColumnNameFetcher.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/DefaultColumnNameFetcher.java @@ -44,12 +44,12 @@ public class DefaultColumnNameFetcher { } if (!association.isBidirectional() && association instanceof org.grails.datastore.mapping.model.types.OneToMany) { - String prefix = namingStrategyWrapper.resolveTableName(property.getOwner().getJavaClass().getSimpleName()); + String prefix = namingStrategyWrapper.resolveTableName(property.getOwner().getRootEntity().getJavaClass().getSimpleName()); return backticksRemover.apply(prefix) + UNDERSCORE + backticksRemover.apply(columnName) + FOREIGN_KEY_SUFFIX; } if (property.isInherited() && property.isBidirectionalManyToOne()) { - return namingStrategyWrapper.resolveColumnName(property.getOwner().getJavaClass().getSimpleName()) + '_' + columnName + FOREIGN_KEY_SUFFIX; + return namingStrategyWrapper.resolveColumnName(property.getOwner().getRootEntity().getJavaClass().getSimpleName()) + '_' + columnName + FOREIGN_KEY_SUFFIX; } return columnName + FOREIGN_KEY_SUFFIX; diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/NamingStrategyWrapper.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/NamingStrategyWrapper.java index 79fa59be8f..1e7d195b4b 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/NamingStrategyWrapper.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/NamingStrategyWrapper.java @@ -39,7 +39,7 @@ public class NamingStrategyWrapper implements PersistentEntityNamingStrategy { return Optional.ofNullable(logicalName) .flatMap(name -> // Safely handle a null return from the strategy by wrapping it in an Optional. - Optional.ofNullable(namingStrategy.toPhysicalColumnName(toIdentifier(name), jdbcEnvironment)) + Optional.ofNullable(namingStrategy.toPhysicalColumnName(toIdentifier(name.replace('.', '_')), jdbcEnvironment)) ) .map(Identifier::getText) // Per Hibernate contract, if the strategy returns null, use the original logical name. @@ -51,7 +51,7 @@ public class NamingStrategyWrapper implements PersistentEntityNamingStrategy { return Optional.ofNullable(logicalName) .flatMap(name -> // Safely handle a null return from the strategy. - Optional.ofNullable(namingStrategy.toPhysicalTableName(toIdentifier(name), jdbcEnvironment)) + Optional.ofNullable(namingStrategy.toPhysicalTableName(toIdentifier(name.replace('.', '_')), jdbcEnvironment)) ) .map(Identifier::getText) // Per Hibernate contract, if the strategy returns null, use the original logical name. diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleValueBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleValueBinder.java index d7bc47369b..fbca633dc5 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleValueBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleValueBinder.java @@ -62,18 +62,29 @@ public class SimpleValueBinder { PersistentProperty parentProperty, SimpleValue simpleValue, String path) { + bindSimpleValue(property, property, parentProperty, simpleValue, path); + } + + public void bindSimpleValue( + @jakarta.annotation.Nonnull PersistentProperty property, + @jakarta.annotation.Nonnull PersistentProperty typeProperty, + PersistentProperty parentProperty, + SimpleValue simpleValue, + String path) { PropertyConfig propertyConfig = ((GrailsHibernatePersistentProperty) property).getMappedForm(); Mapping mapping = null; if (property.getOwner() instanceof GrailsHibernatePersistentEntity persistentEntity) { mapping = persistentEntity.getMappedForm(); } - - final String typeName = property instanceof GrailsHibernatePersistentProperty ghpp ? ghpp.getTypeName() : null; + + final String typeName = typeProperty instanceof GrailsHibernatePersistentProperty ghpp ? ghpp.getTypeName() : null; if (typeName == null) { - Class<?> type = property.getType(); - if (type != null) { - simpleValue.setTypeName(type.getName()); + if (!(typeProperty instanceof org.grails.datastore.mapping.model.types.Association)) { + Class<?> type = typeProperty.getType(); + if (type != null) { + simpleValue.setTypeName(type.getName()); + } } } else { simpleValue.setTypeName(typeName); @@ -121,6 +132,9 @@ public class SimpleValueBinder { Column column = new Column(); columnConfigToColumnBinder.bindColumnConfigToColumn(column, cc, propertyConfig); columnBinder.bindColumn(property, parentProperty, column, cc, path, table); + if (simpleValue instanceof org.hibernate.mapping.DependantValue) { + column.setNullable(true); + } if (table != null) { table.addColumn(column); } diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/TableNameFetcher.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/TableNameFetcher.java index c6b2fdcb33..69f16fa5e1 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/TableNameFetcher.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/TableNameFetcher.java @@ -18,8 +18,13 @@ public class TableNameFetcher { } public String getTableName(GrailsHibernatePersistentEntity domainClass) { + GrailsHibernatePersistentEntity root = (GrailsHibernatePersistentEntity) domainClass.getRootEntity(); + Mapping rootMapping = root.getMappedForm(); Mapping result = domainClass.getMappedForm(); var tableName = result != null ? result.getTableName() : null; + if (tableName == null && rootMapping != null && rootMapping.isTablePerHierarchy()) { + tableName = rootMapping.getTableName(); + } return tableName != null ? tableName :persistentEntityNamingStrategy.resolveTableName(domainClass); } } diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java index fb414a8f56..5a5d7fc95c 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java @@ -177,7 +177,7 @@ public class CollectionSecondPassBinder { String columnName = propConfig.getJoinTable().getKey().getName(); - new SimpleValueColumnBinder().bindSimpleValue(key, "long", columnName, false); + new SimpleValueColumnBinder().bindSimpleValue(key, "long", columnName, true); } else { @@ -213,15 +213,19 @@ public class CollectionSecondPassBinder { } else { // TODO support unidirectional many-to-many } - } else if (property.supportsJoinColumnMapping()) { - bindCollectionWithJoinTable(property, mappings, collection, propConfig, sessionFactoryBeanName); - } else if (property.isUnidirectionalOneToMany()) { // for non-inverse one-to-many, with a not-null fk, add a backref! // there are problems with list and map mappings and join columns relating to duplicate key constraints // TODO change this when HHH-1268 is resolved - bindUnidirectionalOneToMany((org.grails.datastore.mapping.model.types.OneToMany) property, mappings, collection); + if (!property.shouldBindWithForeignKey()) { + bindCollectionWithJoinTable(property, mappings, collection, propConfig, sessionFactoryBeanName); + } else { + bindUnidirectionalOneToMany((org.grails.datastore.mapping.model.types.OneToMany) property, mappings, collection); + } + } else if (property.supportsJoinColumnMapping()) { + bindCollectionWithJoinTable(property, mappings, collection, propConfig, sessionFactoryBeanName); } + forceNullableAndCheckUpdateable(key, property, mappings); } private void bindUnidirectionalOneToMany(org.grails.datastore.mapping.model.types.OneToMany property, @Nonnull InFlightMetadataCollector mappings, Collection collection) { @@ -254,6 +258,7 @@ public class CollectionSecondPassBinder { private void bindCollectionWithJoinTable(HibernateToManyProperty property, @Nonnull InFlightMetadataCollector mappings, Collection collection, PropertyConfig config, String sessionFactoryBeanName) { + collection.setInverse(false); SimpleValue element; final boolean isBasicCollectionType = property instanceof Basic; if (isBasicCollectionType) { @@ -264,7 +269,6 @@ public class CollectionSecondPassBinder { element = new ManyToOne(metadataBuildingContext, collection.getCollectionTable()); bindUnidirectionalOneToManyInverseValues(property, (ManyToOne) element); } - collection.setInverse(false); String columnName; @@ -328,7 +332,7 @@ public class CollectionSecondPassBinder { columnName = joinColumnMappingOptional.get().getName(); } else { - var decapitalize = domainClass.getJavaClass().getSimpleName(); + var decapitalize = domainClass.getRootEntity().getJavaClass().getSimpleName(); columnName = namingStrategy.resolveColumnName(decapitalize) + FOREIGN_KEY_SUFFIX; } @@ -383,7 +387,12 @@ public class CollectionSecondPassBinder { } else { // set type - new SimpleValueBinder(namingStrategy).bindSimpleValue(property, null, key, EMPTY_PATH); + GrailsHibernatePersistentProperty identity = (GrailsHibernatePersistentProperty) refDomainClass.getIdentity(); + if (identity != null) { + new SimpleValueBinder(namingStrategy).bindSimpleValue(property, identity, null, key, EMPTY_PATH); + } else { + new SimpleValueBinder(namingStrategy).bindSimpleValue(property, null, key, EMPTY_PATH); + } } } @@ -423,6 +432,30 @@ public class CollectionSecondPassBinder { } } + private void forceNullableAndCheckUpdateable(DependantValue key, PersistentProperty property, InFlightMetadataCollector mappings) { + Iterator<?> it = key.getColumns().iterator(); + while (it.hasNext()) { + Object next = it.next(); + if (next instanceof Column) { + ((Column) next).setNullable(true); + } + } + + int unidirectionalCount = 0; + PersistentEntity owner = property.getOwner(); + for (PersistentProperty p : owner.getPersistentProperties()) { + if (p instanceof Association association && !association.isBidirectional()) { + unidirectionalCount++; + } + } + + if (unidirectionalCount > 1) { + key.setUpdateable(false); + } else { + key.setUpdateable(true); + } + } + private DependantValue createPrimaryKeyValue(@Nonnull InFlightMetadataCollector mappings, PersistentProperty property, Collection collection, java.util.Map<?, ?> persistentClasses) { KeyValue keyValue; @@ -439,11 +472,10 @@ public class CollectionSecondPassBinder { LOG.debug("[CollectionSecondPassBinder] creating dependant key value to table [" + keyValue.getTable().getName() + "]"); key = new DependantValue(metadataBuildingContext, collection.getCollectionTable(), keyValue); - key.setTypeName(null); - // make nullable and non-updateable key.setNullable(true); - key.setUpdateable(false); + key.setUpdateable(true); + //JPA now requires to check for sorting key.setSorted(collection.isSorted()); return key; diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/NamingStrategyWrapperSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/NamingStrategyWrapperSpec.groovy index 87358288ec..0e91ff3071 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/NamingStrategyWrapperSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/NamingStrategyWrapperSpec.groovy @@ -133,9 +133,10 @@ class NamingStrategyWrapperSpec extends HibernateGormDatastoreSpec { capturedIdentifier.text == decapitalizedOwnerName } - def "should preserve dots for logical column name before passing to wrapped strategy"() { + def "should replace dots with underscores for logical column name before passing to wrapped strategy"() { given: def logicalNameWithDots = "com.example.MyClass.myProperty" + def expectedLogicalName = "com_example_MyClass_myProperty" def expectedPhysicalName = "com_example_my_class_my_property" def capturedIdentifier @@ -149,12 +150,13 @@ class NamingStrategyWrapperSpec extends HibernateGormDatastoreSpec { then: actualResult == expectedPhysicalName - capturedIdentifier.text == logicalNameWithDots + capturedIdentifier.text == expectedLogicalName } - def "should preserve dots for logical table name before passing to wrapped strategy"() { + def "should replace dots with underscores for logical table name before passing to wrapped strategy"() { given: def logicalNameWithDots = "com.example.MyClass" + def expectedLogicalName = "com_example_MyClass" def expectedPhysicalName = "com_example_my_class" def capturedIdentifier @@ -168,7 +170,7 @@ class NamingStrategyWrapperSpec extends HibernateGormDatastoreSpec { then: actualResult == expectedPhysicalName - capturedIdentifier.text == logicalNameWithDots + capturedIdentifier.text == expectedLogicalName } } @@ -177,5 +179,4 @@ class NamingStrategyWrapperSpec extends HibernateGormDatastoreSpec { class Owner { Long id String someProperty -} - +} \ No newline at end of file diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/TableNameFetcherSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/TableNameFetcherSpec.groovy index da49d54082..8582823c49 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/TableNameFetcherSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/TableNameFetcherSpec.groovy @@ -15,9 +15,10 @@ class TableNameFetcherSpec extends Specification { def namingStrategy = Mock(PersistentEntityNamingStrategy) def fetcher = new TableNameFetcher(namingStrategy) def mapping = Mock(Mapping) - def persistentEntity = Mock(GrailsHibernatePersistentEntity) { - getMappedForm() >> mapping - } + def persistentEntity = Mock(GrailsHibernatePersistentEntity) + + persistentEntity.getMappedForm() >> mapping + persistentEntity.getRootEntity() >> persistentEntity // The table name from the mapping can be explicit or null mapping.getTableName() >> tableName @@ -35,4 +36,4 @@ class TableNameFetcherSpec extends Specification { "explicit table name" | "explicit_table_name" | "explicit_table_name" "null table name" | null | "strategy_table_name" } -} +} \ No newline at end of file diff --git a/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/model/config/GormMappingConfigurationStrategy.java b/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/model/config/GormMappingConfigurationStrategy.java index e586cc2b97..ccb7a372dd 100755 --- a/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/model/config/GormMappingConfigurationStrategy.java +++ b/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/model/config/GormMappingConfigurationStrategy.java @@ -290,12 +290,7 @@ public class GormMappingConfigurationStrategy implements MappingConfigurationStr if (associatedEntity.isOwningEntity(association.getOwner())) association.setOwningSide(true); } else if (!(association instanceof Basic)) { - if (associatedEntity.isOwningEntity(association.getOwner())) { - association.setOwningSide(true); - } - else { - association.setOwningSide(false); - } + association.setOwningSide(true); } } }
