diff --git a/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/dirty/GrailsEntityDirtinessStrategy.groovy b/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/dirty/GrailsEntityDirtinessStrategy.groovy index afcbc48c5c..84ae0c3430 100644 --- a/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/dirty/GrailsEntityDirtinessStrategy.groovy +++ b/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/dirty/GrailsEntityDirtinessStrategy.groovy @@ -49,7 +49,7 @@ class GrailsEntityDirtinessStrategy implements CustomEntityDirtinessStrategy { @Override boolean isDirty(Object entity, EntityPersister persister, Session session) { - !session.contains(entity) || !cast(entity).listDirtyPropertyNames().isEmpty() || DirtyCheckingSupport.areEmbeddedDirty(GormEnhancer.findEntity(Hibernate.getClass(entity)), entity) + !session.contains(entity) || cast(entity).hasChanged() || DirtyCheckingSupport.areEmbeddedDirty(GormEnhancer.findEntity(Hibernate.getClass(entity)), entity) } @Override diff --git a/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/support/ClosureEventTriggeringInterceptor.java b/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/support/ClosureEventTriggeringInterceptor.java index 43e0753e14..e425feb236 100644 --- a/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/support/ClosureEventTriggeringInterceptor.java +++ b/grails-datastore-gorm-hibernate5/src/main/groovy/org/grails/orm/hibernate/support/ClosureEventTriggeringInterceptor.java @@ -122,6 +122,7 @@ public void setEventPublisher(ConfigurableApplicationEventPublisher eventPublish public void onSaveOrUpdate(SaveOrUpdateEvent hibernateEvent) throws HibernateException { Object entity = getEntity(hibernateEvent); if(entity != null && proxyHandler.isInitialized(entity)) { + activateDirtyChecking(entity); org.grails.datastore.mapping.engine.event.SaveOrUpdateEvent grailsEvent = new org.grails.datastore.mapping.engine.event.SaveOrUpdateEvent( this.datastore, entity); publishEvent(hibernateEvent, grailsEvent); diff --git a/grails-datastore-gorm-hibernate5/src/test/groovy/grails/gorm/tests/dirtychecking/HibernateUpdateFromListenerSpec.groovy b/grails-datastore-gorm-hibernate5/src/test/groovy/grails/gorm/tests/dirtychecking/HibernateUpdateFromListenerSpec.groovy new file mode 100644 index 0000000000..16807de4b7 --- /dev/null +++ b/grails-datastore-gorm-hibernate5/src/test/groovy/grails/gorm/tests/dirtychecking/HibernateUpdateFromListenerSpec.groovy @@ -0,0 +1,79 @@ +package grails.gorm.tests.dirtychecking + +import grails.gorm.transactions.Rollback +import org.grails.datastore.gorm.events.ConfigurableApplicationEventPublisher +import org.grails.datastore.mapping.core.Datastore +import org.grails.datastore.mapping.engine.event.AbstractPersistenceEvent +import org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener +import org.grails.datastore.mapping.engine.event.PreInsertEvent +import org.grails.datastore.mapping.engine.event.PreUpdateEvent +import org.grails.orm.hibernate.HibernateDatastore +import org.springframework.context.ApplicationEvent +import org.springframework.context.ApplicationEventPublisher +import org.springframework.context.ConfigurableApplicationContext +import org.springframework.transaction.PlatformTransactionManager +import spock.lang.AutoCleanup +import spock.lang.Shared +import spock.lang.Specification +import spock.util.concurrent.PollingConditions + +class HibernateUpdateFromListenerSpec extends Specification { + + @Shared + @AutoCleanup + HibernateDatastore datastore = new HibernateDatastore(Person) + @Shared PlatformTransactionManager transactionManager = datastore.transactionManager + + PersonSaveOrUpdatePersistentEventListener listener + + void setup() { + listener = new PersonSaveOrUpdatePersistentEventListener(datastore) + ApplicationEventPublisher publisher = datastore.applicationEventPublisher + if (publisher instanceof ConfigurableApplicationEventPublisher) { + ((ConfigurableApplicationEventPublisher) publisher).addApplicationListener(listener) + } else if (publisher instanceof ConfigurableApplicationContext) { + ((ConfigurableApplicationContext) publisher).addApplicationListener(listener) + } + } + + @Rollback + void "test the changes made from the listener are saved"() { + when: + Person danny = new Person(name: "Danny", occupation: "manager").save() + + then: + new PollingConditions().eventually {listener.isExecuted && Person.count()} + + when: + datastore.currentSession.flush() + datastore.currentSession.clear() + danny = Person.get(danny.id) + + then: + danny.occupation + danny.occupation.endsWith("listener") + } + + static class PersonSaveOrUpdatePersistentEventListener extends AbstractPersistenceEventListener { + + boolean isExecuted + + protected PersonSaveOrUpdatePersistentEventListener(Datastore datastore) { + super(datastore) + } + + @Override + protected void onPersistenceEvent(AbstractPersistenceEvent event) { + if (event.entityObject instanceof Person) { + Person person = (Person) event.entityObject + person.occupation = person.occupation + " listener" + } + isExecuted = true + } + + @Override + boolean supportsEventType(Class eventType) { + return eventType == PreUpdateEvent || eventType == PreInsertEvent + } + } +}