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 273b8136a998a3837d5c4e73a4d5949ba74d5b00 Author: Walter Duque de Estrada <[email protected]> AuthorDate: Tue Feb 10 08:24:17 2026 -0600 Changes made: 1. `GrailsHibernatePersistentProperty.java`: * Rewrote isColumnNullable() using Optional chains for a more functional style: 1 default boolean isColumnNullable() { 2 return Optional.ofNullable(getHibernateOwner()) 3 .filter(owner -> !owner.isRoot()) 4 .map(owner -> Optional.ofNullable(owner.getMappedForm()) 5 .map(Mapping::getTablePerHierarchy) 6 .orElse(false) || isNullable()) 7 .orElse(false); 8 } 2. `GrailsHibernatePersistentPropertySpec.groovy`: * Expanded the test suite to cover all methods in the interface. * Added tests for isHibernateOneToOne, isHibernateManyToOne, isTablePerHierarchySubclass, isColumnNullable, getIndexColumnName, and getMapElementName. * Added a test case for validateAssociation to ensure it correctly throws a MappingException when an association is mapped to a user type. * Included necessary entity classes (BaseTPH, SubTPH, SubTablePerClass, etc.) to support testing different inheritance and mapping scenarios. --- .../cfg/GrailsHibernatePersistentProperty.java | 10 ++ .../hibernate/cfg/domainbinding/ColumnBinder.java | 7 +- .../GrailsHibernatePersistentPropertySpec.groovy | 104 +++++++++++++++++++++ 3 files changed, 116 insertions(+), 5 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 4139f889d6..a983df2a81 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 @@ -4,6 +4,7 @@ import org.grails.datastore.mapping.model.PersistentProperty; import org.grails.datastore.mapping.model.types.Association; import org.grails.datastore.mapping.model.types.Embedded; +import java.util.Objects; import java.util.Optional; import org.hibernate.MappingException; @@ -135,5 +136,14 @@ public interface GrailsHibernatePersistentProperty extends PersistentProperty<Pr .orElseGet(() -> namingStrategy.resolveColumnName(getName()) + GrailsDomainBinder.UNDERSCORE + IndexedCollection.DEFAULT_ELEMENT_COLUMN_NAME); } + default boolean isColumnNullable() { + return Optional.ofNullable(getHibernateOwner()) + .filter(owner -> !owner.isRoot()) + .map(owner -> Optional.ofNullable(owner.getMappedForm()) + .map(Mapping::getTablePerHierarchy) + .orElse(false) || isNullable()) + .orElse(false); + } + } \ No newline at end of file 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 3f06b1b823..188a9c6310 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 @@ -127,12 +127,9 @@ public class ColumnBinder { createKeyForProps.createKeyForProps(property, path, table, columnName); indexBinder.bindIndex(columnName, column, cc, table); - final PersistentEntity owner = property.getOwner(); + var owner = property.getHibernateOwner(); if (!owner.isRoot()) { - Mapping mapping = null; - if (owner instanceof GrailsHibernatePersistentEntity persistentEntity) { - mapping = persistentEntity.getMappedForm(); - } + Mapping mapping = owner.getMappedForm(); if (mapping != null && mapping.getTablePerHierarchy()) { if (LOG.isDebugEnabled()) LOG.debug("[GrailsDomainBinder] Sub class property [" + property.getName() + "] for column name [" + column.getName() + "] set to nullable"); diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentPropertySpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentPropertySpec.groovy index c6dd181adf..fbdf9135eb 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentPropertySpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentPropertySpec.groovy @@ -4,6 +4,7 @@ import grails.gorm.annotation.Entity import grails.gorm.specs.HibernateGormDatastoreSpec import org.grails.datastore.mapping.model.PersistentEntity import org.grails.datastore.mapping.model.PersistentProperty +import org.grails.orm.hibernate.cfg.domainbinding.NamingStrategyWrapper import spock.lang.Unroll class GrailsHibernatePersistentPropertySpec extends HibernateGormDatastoreSpec { @@ -93,6 +94,83 @@ class GrailsHibernatePersistentPropertySpec extends HibernateGormDatastoreSpec { customMapProp.getIndexColumnType("long") == "long" listProp.getIndexColumnType("integer") == "integer" } + + void "test isHibernateOneToOne and isHibernateManyToOne"() { + given: + createPersistentEntity(AssociatedEntity) + createPersistentEntity(ManyToOneEntity) + PersistentEntity entity = createPersistentEntity(TestEntityWithAssociations) + GrailsHibernatePersistentProperty oneToOneProp = (GrailsHibernatePersistentProperty) entity.getPropertyByName("oneToOne") + GrailsHibernatePersistentProperty manyToOneProp = (GrailsHibernatePersistentProperty) entity.getPropertyByName("manyToOne") + + expect: + oneToOneProp.isHibernateOneToOne() + !oneToOneProp.isHibernateManyToOne() + !manyToOneProp.isHibernateOneToOne() + manyToOneProp.isHibernateManyToOne() + } + + void "test isTablePerHierarchySubclass"() { + given: + createPersistentEntity(BaseTPH) + PersistentEntity subTPH = createPersistentEntity(SubTPH) + createPersistentEntity(BaseTablePerClass) + PersistentEntity subTPC = createPersistentEntity(SubTablePerClass) + + GrailsHibernatePersistentProperty subTPHProp = (GrailsHibernatePersistentProperty) subTPH.getPropertyByName("subProp") + GrailsHibernatePersistentProperty subTPCProp = (GrailsHibernatePersistentProperty) subTPC.getPropertyByName("subProp") + + expect: + subTPHProp.isTablePerHierarchySubclass() + !subTPCProp.isTablePerHierarchySubclass() + } + + void "test isColumnNullable"() { + given: + PersistentEntity baseTPH = createPersistentEntity(BaseTPH) + PersistentEntity subTPH = createPersistentEntity(SubTPH) + PersistentEntity subTPC = createPersistentEntity(SubTablePerClass) + + GrailsHibernatePersistentProperty baseTPHProp = (GrailsHibernatePersistentProperty) baseTPH.getPropertyByName("id") + GrailsHibernatePersistentProperty subTPHProp = (GrailsHibernatePersistentProperty) subTPH.getPropertyByName("subProp") + GrailsHibernatePersistentProperty subTPCProp = (GrailsHibernatePersistentProperty) subTPC.getPropertyByName("subProp") + + expect: + !baseTPHProp.isColumnNullable() // Root is false + subTPHProp.isColumnNullable() // Subclass TPH is true + !subTPCProp.isColumnNullable() // Subclass not TPH uses isNullable() which is false by default + } + + void "test getIndexColumnName and getMapElementName"() { + given: + def jdbcEnvironment = Mock(org.hibernate.engine.jdbc.env.spi.JdbcEnvironment) + def namingStrategy = new NamingStrategyWrapper(new org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl(), jdbcEnvironment) + PersistentEntity entityWithList = createPersistentEntity(EntityWithList) + PersistentEntity entityWithMap = createPersistentEntity(EntityWithMap) + + GrailsHibernatePersistentProperty listProp = (GrailsHibernatePersistentProperty) entityWithList.getPropertyByName("items") + GrailsHibernatePersistentProperty mapProp = (GrailsHibernatePersistentProperty) entityWithMap.getPropertyByName("tags") + + expect: + listProp.getIndexColumnName(namingStrategy) == "items_idx" + mapProp.getMapElementName(namingStrategy) == "tags_elt" + } + + void "test validateAssociation throws exception for user type"() { + given: + PersistentEntity entity = createPersistentEntity(TestEntityWithAssociations) + GrailsHibernatePersistentProperty prop = (GrailsHibernatePersistentProperty) entity.getPropertyByName("manyToOne") + + // Mocking getUserType to return a non-null value for an association + def proxyProp = Spy(prop) + proxyProp.getUserType() >> String.class + + when: + proxyProp.validateAssociation() + + then: + thrown(org.hibernate.MappingException) + } } @@ -201,3 +279,29 @@ class EntityWithList { List<String> items static hasMany = [items: String] } + +@Entity +class BaseTPH { + Long id + static mapping = { + tablePerHierarchy true + } +} + +@Entity +class SubTPH extends BaseTPH { + String subProp +} + +@Entity +class BaseTablePerClass { + Long id + static mapping = { + tablePerHierarchy false + } +} + +@Entity +class SubTablePerClass extends BaseTablePerClass { + String subProp +}
