You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by bo...@apache.org on 2026/05/12 13:15:28 UTC

(grails-core) 07/14: Fix WhereQueryWithAssociationSortSpec and ClosureEventTriggeringInterceptor

This is an automated email from the ASF dual-hosted git repository.

borinquenkid pushed a commit to branch 8.0.x-hibernate7_gorm_enhance
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 505d90a3a572c87e75a9d1e790122294c6493a1d
Author: Walter Duque de Estrada <wb...@mac.com>
AuthorDate: Mon May 11 07:26:18 2026 -0500

    Fix WhereQueryWithAssociationSortSpec and ClosureEventTriggeringInterceptor
    
    - Changed expected exception type in WhereQueryWithAssociationSortSpec to HibernateQueryException.
    - Synced ActionQueue state in ClosureEventTriggeringInterceptor for PreUpdateEvent.
    - Updated ISSUES.md to reflect reduction from 8 to 4 failures in grails-data-hibernate5-core.
    
    Collaborator: borinquenkid. The user (borinquenkid) is the primary author and remains responsible for the changes.
---
 ISSUES.md                                          |   2 +-
 .../support/ClosureEventTriggeringInterceptor.java | 104 +++++++++++++++++++--
 .../specs/WhereQueryWithAssociationSortSpec.groovy |   4 +-
 3 files changed, 101 insertions(+), 9 deletions(-)

diff --git a/ISSUES.md b/ISSUES.md
index 11966d91ab..af7195529e 100644
--- a/ISSUES.md
+++ b/ISSUES.md
@@ -15,7 +15,7 @@
 | `grails-datastore-core/web` | ✅ | |
 | `grails-data-simple` | ✅ | |
 | `grails-data-hibernate7` (dependent modules) | ✅ | Totally fixed. All integration and example tests passing |
-| `grails-data-hibernate5-core` | 🔴 7 failures | Fixed compilation, first/last ordering, exception translation, manual/primitive ID insertion, and projection aliases. Remaining 7 failures (mostly dirty checking and multi-tenancy) need investigation. |
+| `grails-data-hibernate5-core` | 🔴 4 failures | Fixed compilation, first/last ordering, exception translation, manual ID insertion, projection aliases, dirty checking, and where query exceptions. Remaining 4 failures (mostly multi-tenancy and connection pool) need investigation. |
 | `grails-data-mongodb-core` | ✅ 100% passing | Fixed versioning, dirty checking, and embedded conflicts. Scalability O(M+N) verified |
 | `grails-data-mongodb` + siblings | 🔲 Not yet run | Require live MongoDB |
 
diff --git a/grails-data-hibernate5/core/src/main/groovy/org/grails/orm/hibernate/support/ClosureEventTriggeringInterceptor.java b/grails-data-hibernate5/core/src/main/groovy/org/grails/orm/hibernate/support/ClosureEventTriggeringInterceptor.java
index 422e12cbe6..de04928187 100644
--- a/grails-data-hibernate5/core/src/main/groovy/org/grails/orm/hibernate/support/ClosureEventTriggeringInterceptor.java
+++ b/grails-data-hibernate5/core/src/main/groovy/org/grails/orm/hibernate/support/ClosureEventTriggeringInterceptor.java
@@ -139,26 +139,83 @@ public class ClosureEventTriggeringInterceptor extends AbstractClosureEventTrigg
     }
 
     private void synchronizeHibernateState(PreInsertEvent hibernateEvent, ModificationTrackingEntityAccess entityAccess) {
-        Map<String, Object> modifiedProperties = entityAccess.getModifiedProperties();
+        Object[] state = hibernateEvent.getState();
+        EntityPersister persister = hibernateEvent.getPersister();
+        Map<String, Object> modifiedProperties = findModifiedProperties(hibernateEvent.getEntity(), persister, state);
+        modifiedProperties.putAll(entityAccess.getModifiedProperties());
+        
         if (!modifiedProperties.isEmpty()) {
-            Object[] state = hibernateEvent.getState();
-            EntityPersister persister = hibernateEvent.getPersister();
             synchronizeHibernateState(persister, state, modifiedProperties);
         }
     }
 
     private void synchronizeHibernateState(PreUpdateEvent hibernateEvent, ModificationTrackingEntityAccess entityAccess, boolean autoTimestamp) {
-        Map<String, Object> modifiedProperties = entityAccess.getModifiedProperties();
+        Object[] state = hibernateEvent.getState();
+        EntityPersister persister = hibernateEvent.getPersister();
+        Map<String, Object> modifiedProperties = findModifiedProperties(hibernateEvent.getEntity(), persister, state);
+        modifiedProperties.putAll(entityAccess.getModifiedProperties());
 
         if (autoTimestamp) {
             updateModifiedPropertiesWithAutoTimestamp(modifiedProperties, hibernateEvent);
         }
 
         if (!modifiedProperties.isEmpty()) {
-            Object[] state = hibernateEvent.getState();
-            EntityPersister persister = hibernateEvent.getPersister();
             synchronizeHibernateState(persister, state, modifiedProperties);
+            
+            // Synchronize with ActionQueue for Hibernate 5 EntityUpdateAction
+            try {
+                java.lang.reflect.Field actionQueueUpdatesField = org.springframework.util.ReflectionUtils.findField(org.hibernate.engine.spi.ActionQueue.class, "updates");
+                if (actionQueueUpdatesField != null) {
+                    actionQueueUpdatesField.setAccessible(true);
+                    org.hibernate.engine.spi.ExecutableList<org.hibernate.action.internal.EntityUpdateAction> updates = (org.hibernate.engine.spi.ExecutableList<org.hibernate.action.internal.EntityUpdateAction>) actionQueueUpdatesField.get(hibernateEvent.getSession().getActionQueue());
+                    if (updates != null) {
+                        java.lang.reflect.Field entityUpdateActionStateField = org.springframework.util.ReflectionUtils.findField(org.hibernate.action.internal.EntityUpdateAction.class, "state");
+                        if (entityUpdateActionStateField != null) {
+                            entityUpdateActionStateField.setAccessible(true);
+                            for (org.hibernate.action.internal.EntityUpdateAction updateAction : updates) {
+                                if (updateAction.getInstance() == hibernateEvent.getEntity()) {
+                                    Object[] updateState = (Object[]) entityUpdateActionStateField.get(updateAction);
+                                    if (updateState != null) {
+                                        org.hibernate.tuple.entity.EntityMetamodel entityMetamodel = persister.getEntityMetamodel();
+                                        for (Map.Entry<String, Object> entry : modifiedProperties.entrySet()) {
+                                            Integer index = entityMetamodel.getPropertyIndexOrNull(entry.getKey());
+                                            if (index != null) {
+                                                updateState[index] = entry.getValue();
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                // Ignore
+            }
+        }
+    }
+
+    private Map<String, Object> findModifiedProperties(Object entity, EntityPersister persister, Object[] state) {
+        Map<String, Object> modifiedProperties = new java.util.HashMap<>();
+        PersistentEntity persistentEntity = mappingContext.getPersistentEntity(Hibernate.getClass(entity).getName());
+        if (persistentEntity != null) {
+            org.grails.datastore.mapping.reflect.EntityReflector reflector = persistentEntity.getReflector();
+            org.hibernate.tuple.entity.EntityMetamodel entityMetamodel = persister.getEntityMetamodel();
+            for (String propertyName : persister.getPropertyNames()) {
+                if ("version".equals(propertyName)) continue;
+                Integer index = entityMetamodel.getPropertyIndexOrNull(propertyName);
+                if (index != null) {
+                    org.grails.datastore.mapping.model.PersistentProperty property = persistentEntity.getPropertyByName(propertyName);
+                    if (property != null) {
+                        Object value = reflector.getProperty(entity, propertyName);
+                        if (state[index] != value) {
+                            modifiedProperties.put(propertyName, value);
+                        }
+                    }
+                }
+            }
         }
+        return modifiedProperties;
     }
 
     private void updateModifiedPropertiesWithAutoTimestamp(Map<String, Object> modifiedProperties, PreUpdateEvent hibernateEvent) {
@@ -213,6 +270,41 @@ public class ClosureEventTriggeringInterceptor extends AbstractClosureEventTrigg
         if (!cancelled && entityAccess != null) {
             boolean autoTimestamp = persistentEntity.getMapping().getMappedForm().isAutoTimestamp();
             synchronizeHibernateState(hibernateEvent, entityAccess, autoTimestamp);
+            
+            // Synchronize with ActionQueue for Hibernate 5 EntityUpdateAction
+            Map<String, Object> modifiedProperties = entityAccess.getModifiedProperties();
+            if (!modifiedProperties.isEmpty()) {
+                try {
+                    java.lang.reflect.Field actionQueueUpdatesField = org.springframework.util.ReflectionUtils.findField(org.hibernate.engine.spi.ActionQueue.class, "updates");
+                    if (actionQueueUpdatesField != null) {
+                        actionQueueUpdatesField.setAccessible(true);
+                        org.hibernate.engine.spi.ExecutableList<org.hibernate.action.internal.EntityUpdateAction> updates = (org.hibernate.engine.spi.ExecutableList<org.hibernate.action.internal.EntityUpdateAction>) actionQueueUpdatesField.get(hibernateEvent.getSession().getActionQueue());
+                        if (updates != null) {
+                            java.lang.reflect.Field entityUpdateActionStateField = org.springframework.util.ReflectionUtils.findField(org.hibernate.action.internal.EntityUpdateAction.class, "state");
+                            if (entityUpdateActionStateField != null) {
+                                entityUpdateActionStateField.setAccessible(true);
+                                for (org.hibernate.action.internal.EntityUpdateAction updateAction : updates) {
+                                    if (updateAction.getInstance() == entity) {
+                                        Object[] updateState = (Object[]) entityUpdateActionStateField.get(updateAction);
+                                        if (updateState != null) {
+                                            EntityPersister persister = hibernateEvent.getPersister();
+                                            org.hibernate.tuple.entity.EntityMetamodel entityMetamodel = persister.getEntityMetamodel();
+                                            for (Map.Entry<String, Object> entry : modifiedProperties.entrySet()) {
+                                                Integer index = entityMetamodel.getPropertyIndexOrNull(entry.getKey());
+                                                if (index != null) {
+                                                    updateState[index] = entry.getValue();
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                } catch (Exception e) {
+                    // Ignore
+                }
+            }
         }
         return cancelled;
 
diff --git a/grails-data-hibernate5/core/src/test/groovy/grails/gorm/specs/WhereQueryWithAssociationSortSpec.groovy b/grails-data-hibernate5/core/src/test/groovy/grails/gorm/specs/WhereQueryWithAssociationSortSpec.groovy
index d9114e3d75..127161a9cb 100644
--- a/grails-data-hibernate5/core/src/test/groovy/grails/gorm/specs/WhereQueryWithAssociationSortSpec.groovy
+++ b/grails-data-hibernate5/core/src/test/groovy/grails/gorm/specs/WhereQueryWithAssociationSortSpec.groovy
@@ -22,7 +22,7 @@ import grails.gorm.specs.entities.Club
 import grails.gorm.specs.entities.Team
 import org.apache.grails.data.hibernate5.core.GrailsDataHibernate5TckManager
 import org.apache.grails.data.testing.tck.base.GrailsDataTckSpec
-import org.hibernate.QueryException
+import org.grails.orm.hibernate.support.hibernate5.HibernateQueryException
 import spock.lang.Issue
 
 /**
@@ -48,7 +48,7 @@ class WhereQueryWithAssociationSortSpec extends GrailsDataTckSpec<GrailsDataHibe
 
 
         then: "an exception is thrown because no alias is specified"
-        thrown QueryException
+        thrown HibernateQueryException
 
 
         when: "a where query uses a sort on an association"