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 5dc5efb4d00adc8f071b9b4ea25548462ededfb6 Author: Walter B Duque de Estrada <[email protected]> AuthorDate: Mon Feb 2 07:58:24 2026 -0600 Fixed tests --- .../core/HIBERNATE7-UPGRADE-PROGRESS.md | 24 ++++++++-- .../cfg/GrailsHibernatePersistentEntity.java | 15 +++++-- .../cfg/HibernateEmbeddedPersistentEntity.java | 22 ++++++--- .../hibernate/cfg/HibernatePersistentEntity.java | 23 ++++++++++ .../cfg/domainbinding/CompositeIdBinderSpec.groovy | 13 +++--- .../ForeignKeyColumnCountCalculatorSpec.groovy | 3 +- .../cfg/domainbinding/IdentityBinderSpec.groovy | 11 ++--- .../cfg/domainbinding/SimpleIdBinderSpec.groovy | 5 ++- .../HibernateDatastoreSpringInitializerSpec.groovy | 52 +++++++++++----------- 9 files changed, 114 insertions(+), 54 deletions(-) diff --git a/grails-data-hibernate7/core/HIBERNATE7-UPGRADE-PROGRESS.md b/grails-data-hibernate7/core/HIBERNATE7-UPGRADE-PROGRESS.md index 7dc62de505..37eb8ca734 100644 --- a/grails-data-hibernate7/core/HIBERNATE7-UPGRADE-PROGRESS.md +++ b/grails-data-hibernate7/core/HIBERNATE7-UPGRADE-PROGRESS.md @@ -6,9 +6,26 @@ This document summarizes the approaches taken, challenges encountered, and futur ## Resolved Challenges -## Hibernate 7 Key Constraints & Best Practices -- **Proxy Behavior Persistence**: A key lesson from `HibernateProxyHandler7Spec` is that once a class is loaded by Hibernate as a proxy, Hibernate will keep using that proxy instance within the session context even after it has been initialized. Unwrapping is necessary to get to the underlying implementation if needed. -- **Initialization State**: In Hibernate 7, getting a truly uninitialized proxy via `getReference` or `load` requires strict session isolation. If an entity is already in the persistence context (even from a previous transaction if not properly cleared), Hibernate may return the initialized instance instead of a new proxy. + +## New Regressions After Rollback (Feb 1, 2026) + + + + + + + +### 4. Functional Regressions (Attach/Associations) [PENDING] + + + +* **Symptom**: `IllegalArgumentException: Given entity is not associated with the persistence context` in `AttachMethodSpec`. `OneToManySpec` failures where collections are empty. + + + +* **Status**: On hold per user request. + +--- ## Strategy for GrailsDomainBinder Refactoring - **Refactoring Approach**: When modifications to `GrailsDomainBinder` are required, follow this pattern: @@ -26,7 +43,6 @@ This document summarizes the approaches taken, challenges encountered, and futur - **Observation**: Even with a new session, Hibernate 7 seems to return an initialized instance if the entity was persisted earlier in the same test run, possibly due to session factory level caching or improper session disposal in the TCK manager. ## Future Steps -- Fix the `GrailsIncrementGenerator` NPE by ensuring table names are properly resolved in Hibernate 7's new initialization phase. - Fix `UpdateWithProxyPresentSpec` by ensuring a clean state for proxy loading. - Address remaining TCK failures (approx. 16) in the `hibernate 7` module. # Important diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentEntity.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentEntity.java index a06db90127..50c0a4b4f5 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentEntity.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsHibernatePersistentEntity.java @@ -13,8 +13,14 @@ import org.grails.orm.hibernate.cfg.domainbinding.ConfigureDerivedPropertiesCons public interface GrailsHibernatePersistentEntity extends PersistentEntity { Mapping getMappedForm(); + @Override + GrailsHibernatePersistentProperty getIdentity(); + + @Override + GrailsHibernatePersistentProperty[] getCompositeIdentity(); + default String getDiscriminatorValue() { - return Optional.ofNullable(getMappedForm()) + return Optional.ofNullable(getMappedForm()) .map(Mapping::getDiscriminator) .map(DiscriminatorConfig::getValue) .orElse(getName()); @@ -25,8 +31,6 @@ public interface GrailsHibernatePersistentEntity extends PersistentEntity { boolean usesConnectionSource(String dataSourceName); - PersistentProperty[] getCompositeIdentity(); - boolean isAbstract(); @@ -35,6 +39,9 @@ public interface GrailsHibernatePersistentEntity extends PersistentEntity { return (List) getPersistentProperties(); } + @Override + GrailsHibernatePersistentProperty getVersion(); + default List<GrailsHibernatePersistentEntity> getChildEntities(String dataSourceName) { return getMappingContext() .getDirectChildEntities(this) @@ -56,4 +63,4 @@ public interface GrailsHibernatePersistentEntity extends PersistentEntity { getPersistentProperties().forEach(new ConfigureDerivedPropertiesConsumer( getMappedForm())); } -} +} \ No newline at end of file diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/HibernateEmbeddedPersistentEntity.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/HibernateEmbeddedPersistentEntity.java index 2a0727d1d0..934c8fddba 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/HibernateEmbeddedPersistentEntity.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/HibernateEmbeddedPersistentEntity.java @@ -10,6 +10,21 @@ public class HibernateEmbeddedPersistentEntity extends EmbeddedPersistentEntity< return classMapping.getMappedForm(); } + @Override + public GrailsHibernatePersistentProperty getIdentity() { + return (GrailsHibernatePersistentProperty) super.getIdentity(); + } + + @Override + public GrailsHibernatePersistentProperty[] getCompositeIdentity() { + return null; + } + + @Override + public GrailsHibernatePersistentProperty getVersion() { + return (GrailsHibernatePersistentProperty) super.getVersion(); + } + @Override public boolean forGrailsDomainMapping(String dataSourceName) { return false; @@ -20,11 +35,6 @@ public class HibernateEmbeddedPersistentEntity extends EmbeddedPersistentEntity< return ConnectionSourcesSupport.usesConnectionSource(this, dataSourceName); } - @Override - public PersistentProperty[] getCompositeIdentity() { - return null; - } - @Override public boolean isAbstract() { return false; @@ -39,4 +49,4 @@ public class HibernateEmbeddedPersistentEntity extends EmbeddedPersistentEntity< public ClassMapping getMapping() { return classMapping; } -} +} \ No newline at end of file diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/HibernatePersistentEntity.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/HibernatePersistentEntity.java index 37c87e35f5..26be8f29dc 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/HibernatePersistentEntity.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/HibernatePersistentEntity.java @@ -53,6 +53,24 @@ public class HibernatePersistentEntity extends AbstractPersistentEntity<Mapping> return Optional.ofNullable(getMapping()).map(ClassMapping::getMappedForm).orElse(null); } + @Override + public GrailsHibernatePersistentProperty getIdentity() { + return (GrailsHibernatePersistentProperty) identity; + } + + @Override + public GrailsHibernatePersistentProperty[] getCompositeIdentity() { + PersistentProperty[] compositeIdentity = super.getCompositeIdentity(); + if (compositeIdentity == null) { + return null; + } + GrailsHibernatePersistentProperty[] result = new GrailsHibernatePersistentProperty[compositeIdentity.length]; + for (int i = 0; i < compositeIdentity.length; i++) { + result[i] = (GrailsHibernatePersistentProperty) compositeIdentity[i]; + } + return result; + } + private boolean isAnnotatedEntity() { return getJavaClass().isAnnotationPresent(Entity.class); } @@ -67,6 +85,11 @@ public class HibernatePersistentEntity extends AbstractPersistentEntity<Mapping> && isRoot(); } + @Override + public GrailsHibernatePersistentProperty getVersion() { + return (GrailsHibernatePersistentProperty) version; + } + @Override @SuppressWarnings("unchecked") public java.util.List<GrailsHibernatePersistentProperty> getHibernatePersistentProperties() { diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CompositeIdBinderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CompositeIdBinderSpec.groovy index 855b48c1af..6151ffd436 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CompositeIdBinderSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/CompositeIdBinderSpec.groovy @@ -2,6 +2,7 @@ package org.grails.orm.hibernate.cfg.domainbinding import grails.gorm.specs.HibernateGormDatastoreSpec import org.grails.datastore.mapping.model.PersistentProperty +import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentProperty import org.grails.orm.hibernate.cfg.CompositeIdentity import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentEntity import org.hibernate.boot.spi.InFlightMetadataCollector @@ -31,9 +32,9 @@ class CompositeIdBinderSpec extends HibernateGormDatastoreSpec { def compositeIdentity = new CompositeIdentity(propertyNames: ['prop1', 'prop2'] as String[]) - def prop1 = GroovyMock(PersistentProperty) - def prop2 = GroovyMock(PersistentProperty) - def identifierProp = GroovyMock(PersistentProperty) + def prop1 = GroovyMock(GrailsHibernatePersistentProperty) + def prop2 = GroovyMock(GrailsHibernatePersistentProperty) + def identifierProp = GroovyMock(GrailsHibernatePersistentProperty) domainClass.getPropertyByName("prop1") >> prop1 domainClass.getPropertyByName("prop2") >> prop2 domainClass.getIdentity() >> identifierProp @@ -60,9 +61,9 @@ class CompositeIdBinderSpec extends HibernateGormDatastoreSpec { root.setEntityName("MyEntity") def mappings = metadataBuildingContext.getMetadataCollector() - def prop1 = GroovyMock(PersistentProperty) - def identifierProp = GroovyMock(PersistentProperty) - domainClass.getCompositeIdentity() >> ([prop1] as PersistentProperty[]) + def prop1 = GroovyMock(GrailsHibernatePersistentProperty) + def identifierProp = GroovyMock(GrailsHibernatePersistentProperty) + domainClass.getCompositeIdentity() >> ([prop1] as GrailsHibernatePersistentProperty[]) domainClass.getIdentity() >> identifierProp domainClass.getName() >> "MyEntity" diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ForeignKeyColumnCountCalculatorSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ForeignKeyColumnCountCalculatorSpec.groovy index a327560755..5a26074a1b 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ForeignKeyColumnCountCalculatorSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ForeignKeyColumnCountCalculatorSpec.groovy @@ -3,6 +3,7 @@ package org.grails.orm.hibernate.cfg.domainbinding import org.grails.datastore.mapping.model.PersistentEntity import org.grails.datastore.mapping.model.PersistentProperty import org.grails.datastore.mapping.model.types.ToOne +import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentProperty import org.grails.orm.hibernate.cfg.HibernatePersistentEntity import spock.lang.Specification import spock.lang.Unroll @@ -29,7 +30,7 @@ class ForeignKeyColumnCountCalculatorSpec extends Specification { // Mocks for a ToOne association with a composite ID of length 2 def toOneCompositeIdProp = Mock(ToOne) def associatedEntityCompositeId = Mock(HibernatePersistentEntity) - def compositeId = [Mock(PersistentProperty), Mock(PersistentProperty)] as PersistentProperty[] + def compositeId = [Mock(GrailsHibernatePersistentProperty), Mock(GrailsHibernatePersistentProperty)] as GrailsHibernatePersistentProperty[] refDomainClass.getPropertyByName("toOneComposite") >> toOneCompositeIdProp toOneCompositeIdProp.getAssociatedEntity() >> associatedEntityCompositeId associatedEntityCompositeId.getCompositeIdentity() >> compositeId diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/IdentityBinderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/IdentityBinderSpec.groovy index 132e48d8ae..a282a80533 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/IdentityBinderSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/IdentityBinderSpec.groovy @@ -1,6 +1,7 @@ package org.grails.orm.hibernate.cfg.domainbinding import org.grails.datastore.mapping.model.PersistentProperty +import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentProperty import org.grails.orm.hibernate.cfg.CompositeIdentity import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentEntity import org.grails.orm.hibernate.cfg.Identity @@ -27,7 +28,7 @@ class IdentityBinderSpec extends HibernateGormDatastoreSpec { def domainClass = Mock(GrailsHibernatePersistentEntity) def root = new RootClass(getGrailsDomainBinder().getMetadataBuildingContext()) def mappings = Mock(InFlightMetadataCollector) - def identifierProp = Mock(PersistentProperty) + def identifierProp = Mock(GrailsHibernatePersistentProperty) domainClass.getIdentity() >> identifierProp domainClass.getCompositeIdentity() >> null @@ -43,7 +44,7 @@ class IdentityBinderSpec extends HibernateGormDatastoreSpec { def domainClass = Mock(GrailsHibernatePersistentEntity) def root = new RootClass(getGrailsDomainBinder().getMetadataBuildingContext()) def mappings = Mock(InFlightMetadataCollector) - def compositeProps = [Mock(PersistentProperty)] as PersistentProperty[] + def compositeProps = [Mock(GrailsHibernatePersistentProperty)] as GrailsHibernatePersistentProperty[] domainClass.getCompositeIdentity() >> compositeProps when: @@ -77,7 +78,7 @@ class IdentityBinderSpec extends HibernateGormDatastoreSpec { def gormMapping = Mock(Mapping) def identity = new Identity(name: "foo") gormMapping.getIdentity() >> identity - def identifierProp = Mock(PersistentProperty) + def identifierProp = Mock(GrailsHibernatePersistentProperty) domainClass.getPropertyByName("foo") >> identifierProp domainClass.getIdentity() >> identifierProp domainClass.getName() >> "MyEntity" @@ -115,7 +116,7 @@ class IdentityBinderSpec extends HibernateGormDatastoreSpec { def gormMapping = Mock(Mapping) def identity = new Identity(name: "MyEntity") gormMapping.getIdentity() >> identity - def identifierProp = Mock(PersistentProperty) + def identifierProp = Mock(GrailsHibernatePersistentProperty) domainClass.getIdentity() >> identifierProp domainClass.getName() >> "MyEntity" @@ -136,7 +137,7 @@ class IdentityBinderSpec extends HibernateGormDatastoreSpec { def gormMapping = Mock(Mapping) def identity = new Identity() gormMapping.getIdentity() >> identity - def identifierProp = Mock(PersistentProperty) + def identifierProp = Mock(GrailsHibernatePersistentProperty) domainClass.getIdentity() >> identifierProp domainClass.getName() >> "MyEntity" diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleIdBinderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleIdBinderSpec.groovy index 4015da0d1a..9eed3c6014 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleIdBinderSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleIdBinderSpec.groovy @@ -2,6 +2,7 @@ package org.grails.orm.hibernate.cfg.domainbinding import grails.gorm.specs.HibernateGormDatastoreSpec import org.grails.datastore.mapping.model.PersistentProperty +import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentProperty import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentEntity import org.grails.orm.hibernate.cfg.Identity import org.hibernate.boot.spi.MetadataBuildingContext @@ -46,7 +47,7 @@ class SimpleIdBinderSpec extends HibernateGormDatastoreSpec { def mapping = Mock(org.grails.orm.hibernate.cfg.Mapping) { isTablePerConcreteClass() >> false } - def testProperty = Mock(PersistentProperty) { + def testProperty = Mock(GrailsHibernatePersistentProperty) { getName() >> "id" getOwner() >> Mock(GrailsHibernatePersistentEntity) { getMappedForm() >> mapping @@ -74,7 +75,7 @@ class SimpleIdBinderSpec extends HibernateGormDatastoreSpec { def mapping = Mock(org.grails.orm.hibernate.cfg.Mapping) { isTablePerConcreteClass() >> true } - def testProperty = Mock(PersistentProperty) { + def testProperty = Mock(GrailsHibernatePersistentProperty) { getName() >> "id" getOwner() >> Mock(GrailsHibernatePersistentEntity) { getMappedForm() >> mapping diff --git a/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy b/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy index 88fb4c628d..a969202e44 100644 --- a/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy +++ b/grails-data-hibernate7/grails-plugin/src/test/groovy/grails/orm/bootstrap/HibernateDatastoreSpringInitializerSpec.groovy @@ -62,32 +62,32 @@ class HibernateDatastoreSpringInitializerSpec extends Specification{ and:"Each domain has the correct data source(s)" Person.withNewSession { Person.count() == 0 } - Person.withNewSession { Session s -> - assert s.connection().metaData.getURL() == "jdbc:h2:mem:people" - return true - } - Book.withNewSession { Book.count() == 0 } - Book.withNewSession { Session s -> - assert s.connection().metaData.getURL() == "jdbc:h2:mem:books" - return true - } - Book.moreBooks.withNewSession { Session s -> - assert s.connection().metaData.getURL() == "jdbc:h2:mem:moreBooks" - return true - } - Author.withNewSession { Author.count() == 0 } - Author.withNewSession { Session s -> - assert s.connection().metaData.getURL() == "jdbc:h2:mem:people" - return true - } - Author.books.withNewSession { Session s -> - assert s.connection().metaData.getURL() == "jdbc:h2:mem:books" - return true - } - Author.moreBooks.withNewSession { Session s -> - assert s.connection().metaData.getURL() == "jdbc:h2:mem:moreBooks" - return true - } + Person.withNewSession { Session s -> + assert s.doReturningWork { it.getMetaData().getURL() } == "jdbc:h2:mem:people" + return true + } + Book.withNewSession { Book.count() == 0 } + Book.withNewSession { Session s -> + assert s.doReturningWork { it.getMetaData().getURL() } == "jdbc:h2:mem:books" + return true + } + Book.moreBooks.withNewSession { Session s -> + assert s.doReturningWork { it.getMetaData().getURL() } == "jdbc:h2:mem:moreBooks" + return true + } + Author.withNewSession { Author.count() == 0 } + Author.withNewSession { Session s -> + assert s.doReturningWork { it.getMetaData().getURL() } == "jdbc:h2:mem:people" + return true + } + Author.books.withNewSession { Session s -> + assert s.doReturningWork { it.getMetaData().getURL() } == "jdbc:h2:mem:books" + return true + } + Author.moreBooks.withNewSession { Session s -> + assert s.doReturningWork { it.getMetaData().getURL() } == "jdbc:h2:mem:moreBooks" + return true + } } }
