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"