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 7d6d589efcca60c045b1111763e491a1132e1a7b
Author: Walter Duque de Estrada <wbdu...@mac.com>
AuthorDate: Wed Sep 3 18:23:04 2025 -0500

    Validation
---
 .../orm/hibernate/AbstractHibernateDatastore.java  |  12 ++
 .../hibernate/support/HibernateRuntimeUtils.groovy |  25 +--
 .../groovy/grails/gorm/specs/ValidationSpec.groovy | 171 -------------------
 .../data/testing/tck/tests/ValidationSpec.groovy   | 186 +++++++++++++++++++--
 4 files changed, 200 insertions(+), 194 deletions(-)

diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/AbstractHibernateDatastore.java
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/AbstractHibernateDatastore.java
index 458dd4832b..f59114e96a 100644
--- 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/AbstractHibernateDatastore.java
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/AbstractHibernateDatastore.java
@@ -271,6 +271,18 @@ public abstract class AbstractHibernateDatastore extends 
AbstractDatastore imple
         return autoTimestampEventListener;
     }
 
+    @Override
+    public boolean hasCurrentSession() {
+        // Consider a session present only if a bound session exists and is 
connected
+        org.grails.datastore.mapping.transactions.SessionHolder sessionHolder =
+                (org.grails.datastore.mapping.transactions.SessionHolder)
+                        
org.springframework.transaction.support.TransactionSynchronizationManager.getResource(this);
+        if (sessionHolder == null) {
+            return false;
+        }
+        return sessionHolder.getValidatedSession() != null;
+    }
+
     /**
      * @return The data source name being used
      */
diff --git 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/support/HibernateRuntimeUtils.groovy
 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/support/HibernateRuntimeUtils.groovy
index 9046518e90..edd3afde28 100644
--- 
a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/support/HibernateRuntimeUtils.groovy
+++ 
b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/support/HibernateRuntimeUtils.groovy
@@ -51,16 +51,21 @@ class HibernateRuntimeUtils {
         def errors = new ValidationErrors(target)
 
         Errors originalErrors = isGormValidateable ? 
((GormValidateable)target).getErrors() : (Errors) mc.getProperty(target, 
GormProperties.ERRORS)
-        for (Object o in originalErrors.fieldErrors) {
-            FieldError fe = (FieldError)o
-            if (fe.isBindingFailure()) {
-                errors.addError(new FieldError(fe.getObjectName(),
-                        fe.field,
-                        fe.rejectedValue,
-                        fe.bindingFailure,
-                        fe.codes,
-                        fe.arguments,
-                        fe.defaultMessage))
+        // Copy binding failures and any existing object-level errors
+        for (Object o in originalErrors.allErrors) {
+            if (o instanceof FieldError) {
+                FieldError fe = (FieldError)o
+                if (fe.isBindingFailure()) {
+                    errors.addError(new FieldError(fe.getObjectName(),
+                            fe.field,
+                            fe.rejectedValue,
+                            fe.bindingFailure,
+                            fe.codes,
+                            fe.arguments,
+                            fe.defaultMessage))
+                }
+            } else {
+                errors.addError((org.springframework.validation.ObjectError) o)
             }
         }
 
diff --git 
a/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/ValidationSpec.groovy
 
b/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/ValidationSpec.groovy
deleted file mode 100644
index 9478aad07f..0000000000
--- 
a/grails-data-hibernate6/core/src/test/groovy/grails/gorm/specs/ValidationSpec.groovy
+++ /dev/null
@@ -1,171 +0,0 @@
-package grails.gorm.specs
-
-import org.apache.grails.data.hibernate6.core.GrailsDataHibernate6TckManager
-import org.apache.grails.data.testing.tck.base.GrailsDataTckSpec
-import org.apache.grails.data.testing.tck.domains.ChildEntity
-import 
org.apache.grails.data.testing.tck.domains.ClassWithListArgBeforeValidate
-import org.apache.grails.data.testing.tck.domains.ClassWithNoArgBeforeValidate
-import 
org.apache.grails.data.testing.tck.domains.ClassWithOverloadedBeforeValidate
-import org.apache.grails.data.testing.tck.domains.TestEntity
-import 
org.springframework.transaction.support.TransactionSynchronizationManager
-
-/**
- * Tests validation semantics.
- */
-class ValidationSpec extends GrailsDataTckSpec<GrailsDataHibernate6TckManager> 
{
-
-    void setupSpec() {
-        manager.addAllDomainClasses([ClassWithListArgBeforeValidate, 
ClassWithNoArgBeforeValidate,
-                                      ClassWithOverloadedBeforeValidate])
-    }
-
-    void "Test validate() method"() {
-        // test assumes name cannot be blank
-        given:
-        def t
-
-        when:
-        t = new TestEntity(name: "")
-        boolean validationResult = t.validate()
-        def errors = t.errors
-
-        then:
-        !validationResult
-        t.hasErrors()
-        errors != null
-        errors.hasErrors()
-
-        when:
-        t.clearErrors()
-
-        then:
-        !t.hasErrors()
-    }
-
-
-    void "Test that validate is called on save()"() {
-        given:
-        def t
-
-        when:
-        t = new TestEntity(name: "")
-
-        then:
-        t.save() == null
-        t.hasErrors() == true
-        0 == TestEntity.count()
-
-        when:
-        t.clearErrors()
-        t.name = "Bob"
-        t.age = 45
-        t.child = new ChildEntity(name: "Fred")
-        t = t.save()
-
-        then:
-        t != null
-        1 == TestEntity.count()
-    }
-
-    void "Test beforeValidate gets called on save()"() {
-        given:
-        def entityWithNoArgBeforeValidateMethod
-        def entityWithListArgBeforeValidateMethod
-        def entityWithOverloadedBeforeValidateMethod
-
-        when:
-        entityWithNoArgBeforeValidateMethod = new 
ClassWithNoArgBeforeValidate()
-        entityWithListArgBeforeValidateMethod = new 
ClassWithListArgBeforeValidate()
-        entityWithOverloadedBeforeValidateMethod = new 
ClassWithOverloadedBeforeValidate()
-        entityWithNoArgBeforeValidateMethod.save()
-        entityWithListArgBeforeValidateMethod.save()
-        entityWithOverloadedBeforeValidateMethod.save()
-
-        then:
-        1 == entityWithNoArgBeforeValidateMethod.noArgCounter
-        1 == entityWithListArgBeforeValidateMethod.listArgCounter
-        1 == entityWithOverloadedBeforeValidateMethod.noArgCounter
-        0 == entityWithOverloadedBeforeValidateMethod.listArgCounter
-    }
-
-    void "Test beforeValidate gets called on validate()"() {
-        given:
-        def entityWithNoArgBeforeValidateMethod
-        def entityWithListArgBeforeValidateMethod
-        def entityWithOverloadedBeforeValidateMethod
-
-        when:
-        entityWithNoArgBeforeValidateMethod = new 
ClassWithNoArgBeforeValidate()
-        entityWithListArgBeforeValidateMethod = new 
ClassWithListArgBeforeValidate()
-        entityWithOverloadedBeforeValidateMethod = new 
ClassWithOverloadedBeforeValidate()
-        entityWithNoArgBeforeValidateMethod.validate()
-        entityWithListArgBeforeValidateMethod.validate()
-        entityWithOverloadedBeforeValidateMethod.validate()
-
-        then:
-        1 == entityWithNoArgBeforeValidateMethod.noArgCounter
-        1 == entityWithListArgBeforeValidateMethod.listArgCounter
-        1 == entityWithOverloadedBeforeValidateMethod.noArgCounter
-        0 == entityWithOverloadedBeforeValidateMethod.listArgCounter
-    }
-
-    void "Test beforeValidate gets called on validate() and passing a list of 
field names to validate"() {
-        given:
-        def entityWithNoArgBeforeValidateMethod
-        def entityWithListArgBeforeValidateMethod
-        def entityWithOverloadedBeforeValidateMethod
-
-        when:
-        entityWithNoArgBeforeValidateMethod = new 
ClassWithNoArgBeforeValidate()
-        entityWithListArgBeforeValidateMethod = new 
ClassWithListArgBeforeValidate()
-        entityWithOverloadedBeforeValidateMethod = new 
ClassWithOverloadedBeforeValidate()
-        entityWithNoArgBeforeValidateMethod.validate(['name'])
-        entityWithListArgBeforeValidateMethod.validate(['name'])
-        entityWithOverloadedBeforeValidateMethod.validate(['name'])
-
-        then:
-        1 == entityWithNoArgBeforeValidateMethod.noArgCounter
-        1 == entityWithListArgBeforeValidateMethod.listArgCounter
-        0 == entityWithOverloadedBeforeValidateMethod.noArgCounter
-        1 == entityWithOverloadedBeforeValidateMethod.listArgCounter
-        ['name'] == 
entityWithOverloadedBeforeValidateMethod.propertiesPassedToBeforeValidate
-    }
-
-    void "Test that validate works without a bound Session"() {
-
-        given:
-        def t
-
-        when:
-        manager.session.disconnect()
-        def resource
-        if 
(TransactionSynchronizationManager.hasResource(manager.session.datastore.sessionFactory))
 {
-            resource = 
TransactionSynchronizationManager.unbindResource(manager.session.datastore.sessionFactory)
-        }
-
-        t = new TestEntity(name: "")
-
-        then:
-        
TransactionSynchronizationManager.getResource(manager.session.datastore.sessionFactory)
 == null
-        t.save() == null
-        t.hasErrors() == true
-
-        when:
-        
TransactionSynchronizationManager.bindResource(manager.session.datastore.sessionFactory,
 resource)
-
-        then:
-        1 == t.errors.allErrors.size()
-        0 == TestEntity.count()
-
-        when:
-        t.clearErrors()
-        t.name = "Bob"
-        t.age = 45
-        t.child = new ChildEntity(name: "Fred")
-        t = t.save(flush: true)
-
-        then:
-        t != null
-        1 == TestEntity.count()
-    }
-}
diff --git 
a/grails-datamapping-tck/src/main/groovy/org/apache/grails/data/testing/tck/tests/ValidationSpec.groovy
 
b/grails-datamapping-tck/src/main/groovy/org/apache/grails/data/testing/tck/tests/ValidationSpec.groovy
index 8761d93b3e..a38e5b5885 100644
--- 
a/grails-datamapping-tck/src/main/groovy/org/apache/grails/data/testing/tck/tests/ValidationSpec.groovy
+++ 
b/grails-datamapping-tck/src/main/groovy/org/apache/grails/data/testing/tck/tests/ValidationSpec.groovy
@@ -18,8 +18,8 @@
  */
 package org.apache.grails.data.testing.tck.tests
 
-import spock.lang.IgnoreIf
-import spock.lang.PendingFeatureIf
+import grails.gorm.transactions.Rollback
+import 
org.springframework.transaction.support.TransactionSynchronizationManager
 import spock.lang.Unroll
 
 import org.springframework.validation.Validator
@@ -41,11 +41,165 @@ class ValidationSpec extends GrailsDataTckSpec {
 
     void setupSpec() {
         manager.addAllDomainClasses([ClassWithListArgBeforeValidate, 
ClassWithNoArgBeforeValidate,
-                                 ClassWithOverloadedBeforeValidate, 
TestEntity, ChildEntity, Task])
+                                     ClassWithOverloadedBeforeValidate, 
TestEntity,Task])
+    }
+
+    @Rollback
+    void "Test validate() method"() {
+        // test assumes name cannot be blank
+        given:
+        def t
+
+        when:
+        t = new TestEntity(name: "")
+        boolean validationResult = t.validate()
+        def errors = t.errors
+
+        then:
+        !validationResult
+        t.hasErrors()
+        errors != null
+        errors.hasErrors()
+
+        when:
+        t.clearErrors()
+
+        then:
+        !t.hasErrors()
+    }
+
+
+    @Rollback
+    void "Test that validate is called on save()"() {
+        given:
+        def t
+
+        when:
+        t = new TestEntity(name: "")
+
+        then:
+        t.save() == null
+        t.hasErrors() == true
+        0 == TestEntity.count()
+
+        when:
+        t.clearErrors()
+        t.name = "Bob"
+        t.age = 45
+        t.child = new ChildEntity(name: "Fred")
+        t = t.save()
+
+        then:
+        t != null
+        1 == TestEntity.count()
+    }
+
+    @Rollback
+    void "Test beforeValidate gets called on save()"() {
+        given:
+        def entityWithNoArgBeforeValidateMethod
+        def entityWithListArgBeforeValidateMethod
+        def entityWithOverloadedBeforeValidateMethod
+
+        when:
+        entityWithNoArgBeforeValidateMethod = new 
ClassWithNoArgBeforeValidate()
+        entityWithListArgBeforeValidateMethod = new 
ClassWithListArgBeforeValidate()
+        entityWithOverloadedBeforeValidateMethod = new 
ClassWithOverloadedBeforeValidate()
+        entityWithNoArgBeforeValidateMethod.save()
+        entityWithListArgBeforeValidateMethod.save()
+        entityWithOverloadedBeforeValidateMethod.save()
+
+        then:
+        1 == entityWithNoArgBeforeValidateMethod.noArgCounter
+        1 == entityWithListArgBeforeValidateMethod.listArgCounter
+        1 == entityWithOverloadedBeforeValidateMethod.noArgCounter
+        0 == entityWithOverloadedBeforeValidateMethod.listArgCounter
+    }
+
+    void "Test beforeValidate gets called on validate()"() {
+        given:
+        def entityWithNoArgBeforeValidateMethod
+        def entityWithListArgBeforeValidateMethod
+        def entityWithOverloadedBeforeValidateMethod
+
+        when:
+        entityWithNoArgBeforeValidateMethod = new 
ClassWithNoArgBeforeValidate()
+        entityWithListArgBeforeValidateMethod = new 
ClassWithListArgBeforeValidate()
+        entityWithOverloadedBeforeValidateMethod = new 
ClassWithOverloadedBeforeValidate()
+        entityWithNoArgBeforeValidateMethod.validate()
+        entityWithListArgBeforeValidateMethod.validate()
+        entityWithOverloadedBeforeValidateMethod.validate()
+
+        then:
+        1 == entityWithNoArgBeforeValidateMethod.noArgCounter
+        1 == entityWithListArgBeforeValidateMethod.listArgCounter
+        1 == entityWithOverloadedBeforeValidateMethod.noArgCounter
+        0 == entityWithOverloadedBeforeValidateMethod.listArgCounter
+    }
+
+    void "Test beforeValidate gets called on validate() and passing a list of 
field names to validate"() {
+        given:
+        def entityWithNoArgBeforeValidateMethod
+        def entityWithListArgBeforeValidateMethod
+        def entityWithOverloadedBeforeValidateMethod
+
+        when:
+        entityWithNoArgBeforeValidateMethod = new 
ClassWithNoArgBeforeValidate()
+        entityWithListArgBeforeValidateMethod = new 
ClassWithListArgBeforeValidate()
+        entityWithOverloadedBeforeValidateMethod = new 
ClassWithOverloadedBeforeValidate()
+        entityWithNoArgBeforeValidateMethod.validate(['name'])
+        entityWithListArgBeforeValidateMethod.validate(['name'])
+        entityWithOverloadedBeforeValidateMethod.validate(['name'])
+
+        then:
+        1 == entityWithNoArgBeforeValidateMethod.noArgCounter
+        1 == entityWithListArgBeforeValidateMethod.listArgCounter
+        0 == entityWithOverloadedBeforeValidateMethod.noArgCounter
+        1 == entityWithOverloadedBeforeValidateMethod.listArgCounter
+        ['name'] == 
entityWithOverloadedBeforeValidateMethod.propertiesPassedToBeforeValidate
+    }
+
+    @Rollback
+    void "Test that validate works without a bound Session"() {
+
+        given:
+        def t
+
+        when:
+        manager.session.disconnect()
+        def resource
+        if 
(TransactionSynchronizationManager.hasResource(manager.session.datastore.sessionFactory))
 {
+            resource = 
TransactionSynchronizationManager.unbindResource(manager.session.datastore.sessionFactory)
+        }
+
+        t = new TestEntity(name: "")
+
+        then:
+        
TransactionSynchronizationManager.getResource(manager.session.datastore.sessionFactory)
 == null
+        t.save() == null
+        t.hasErrors() == true
+
+        when:
+        
TransactionSynchronizationManager.bindResource(manager.session.datastore.sessionFactory,
 resource)
+
+        then:
+        1 == t.errors.allErrors.size()
+        0 == TestEntity.count()
+
+        when:
+        t.clearErrors()
+        t.name = "Bob"
+        t.age = 45
+        t.child = new ChildEntity(name: "Fred")
+        t = t.save(flush: true)
+
+        then:
+        t != null
+        1 == TestEntity.count()
     }
 
     // Hibernate did not originally have this test and it fails for it
-    @PendingFeatureIf({ System.getProperty('hibernate5.gorm.suite') })
+    @Rollback
     void 'Test validating an object that has had values rejected with an 
ObjectError'() {
         given:
         def t = new TestEntity(name: 'someName')
@@ -61,7 +215,7 @@ class ValidationSpec extends GrailsDataTckSpec {
     }
 
     // Hibernate did not originally have this test and it fails for it
-    @PendingFeatureIf({ System.getProperty('hibernate5.gorm.suite') })
+    @Rollback
     void 'Test disable validation'() {
         // test assumes name cannot be blank
         given:
@@ -79,6 +233,7 @@ class ValidationSpec extends GrailsDataTckSpec {
         errors.hasErrors()
 
         when:
+        t = new TestEntity(name: '', child: new ChildEntity(name: 'child'))
         t.save(validate: false, flush: true)
 
         then:
@@ -86,6 +241,7 @@ class ValidationSpec extends GrailsDataTckSpec {
         !t.hasErrors()
     }
 
+    @Rollback
     void 'Test validate() method'() {
         // test assumes name cannot be blank
         given:
@@ -109,6 +265,7 @@ class ValidationSpec extends GrailsDataTckSpec {
         !t.hasErrors()
     }
 
+    @Rollback
     void 'Test that validate is called on save()'() {
 
         given:
@@ -134,6 +291,7 @@ class ValidationSpec extends GrailsDataTckSpec {
         1 == TestEntity.count()
     }
 
+    @Rollback
     void 'Test beforeValidate gets called on save()'() {
         given:
         def entityWithNoArgBeforeValidateMethod
@@ -155,6 +313,7 @@ class ValidationSpec extends GrailsDataTckSpec {
         0 == entityWithOverloadedBeforeValidateMethod.listArgCounter
     }
 
+    @Rollback
     void 'Test beforeValidate gets called on validate()'() {
         given:
         def entityWithNoArgBeforeValidateMethod
@@ -176,6 +335,7 @@ class ValidationSpec extends GrailsDataTckSpec {
         0 == entityWithOverloadedBeforeValidateMethod.listArgCounter
     }
 
+    @Rollback
     void 'Test beforeValidate gets called on validate() and passing a list of 
field names to validate'() {
         given:
         def entityWithNoArgBeforeValidateMethod
@@ -198,24 +358,22 @@ class ValidationSpec extends GrailsDataTckSpec {
         ['name'] == 
entityWithOverloadedBeforeValidateMethod.propertiesPassedToBeforeValidate
     }
 
-    @IgnoreIf({
-        Boolean.getBoolean('neo4j.gorm.suite') || // neo4j requires a 
transaction present for inserts
-                System.getProperty('hibernate5.gorm.suite') // Hibernate has a 
custom version of this test
-    })
+    @Unroll
     void 'Test that validate works without a bound Session'() {
         given:
         def t
+        def initialCount = TestEntity.count()
 
         when:
         manager.session.disconnect()
         t = new TestEntity(name: '')
 
         then:
-        !manager.session.datastore.hasCurrentSession()
+        !manager.session.isConnected()
         t.save() == null
         t.hasErrors() == true
         1 == t.errors.allErrors.size()
-        0 == TestEntity.count()
+        TestEntity.count() == initialCount
 
         when:
         t.clearErrors()
@@ -225,11 +383,13 @@ class ValidationSpec extends GrailsDataTckSpec {
         t = t.save(flush: true)
 
         then:
-        !manager.session.datastore.hasCurrentSession()
+        !manager.session.isConnected()
         t != null
-        1 == TestEntity.count()
+        TestEntity.count() == initialCount + 1
     }
 
+
+    @Unroll
     void 'Two parameter validate is called on entity validator if it 
implements Validator interface'() {
         given:
         def mockValidator = Mock(Validator)

Reply via email to