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..e9c442c614 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 @@ -30,6 +30,8 @@ import org.hibernate.Session import org.hibernate.engine.spi.SessionImplementor import org.hibernate.engine.spi.Status import org.hibernate.persister.entity.EntityPersister +import org.slf4j.Logger +import org.slf4j.LoggerFactory /** * A class to customize Hibernate dirtiness based on Grails {@link DirtyCheckable} interface @@ -42,6 +44,8 @@ import org.hibernate.persister.entity.EntityPersister @CompileStatic class GrailsEntityDirtinessStrategy implements CustomEntityDirtinessStrategy { + protected static final Logger LOG = LoggerFactory.getLogger(GrailsEntityDirtinessStrategy.class) + @Override boolean canDirtyCheck(Object entity, EntityPersister persister, Session session) { return entity instanceof DirtyCheckable @@ -56,6 +60,30 @@ class GrailsEntityDirtinessStrategy implements CustomEntityDirtinessStrategy { void resetDirty(Object entity, EntityPersister persister, Session session) { if (canDirtyCheck(entity, persister, session)) { cast(entity).trackChanges() + try { + PersistentEntity persistentEntity = GormEnhancer.findEntity(Hibernate.getClass(entity)) + if (persistentEntity != null) { + resetDirtyEmbeddedObjects(persistentEntity, entity, persister, session) + } + } catch (IllegalStateException e) { + if (LOG.isDebugEnabled()) { + LOG.debug(e.message, e) + } + } + } + } + + private void resetDirtyEmbeddedObjects(PersistentEntity persistentEntity, + Object entity, + EntityPersister persister, + Session session) { + + if (DirtyCheckingSupport.areEmbeddedDirty(persistentEntity, entity)) { + final associations = persistentEntity.getEmbedded() + for (Embedded a in associations) { + final value = a.reader.read(entity) + resetDirty(value, persister, session) + } } } diff --git a/grails-datastore-gorm-hibernate5/src/test/groovy/grails/gorm/tests/dirtychecking/HibernateDirtyCheckingSpec.groovy b/grails-datastore-gorm-hibernate5/src/test/groovy/grails/gorm/tests/dirtychecking/HibernateDirtyCheckingSpec.groovy index f9075d5217..cb6086afe1 100644 --- a/grails-datastore-gorm-hibernate5/src/test/groovy/grails/gorm/tests/dirtychecking/HibernateDirtyCheckingSpec.groovy +++ b/grails-datastore-gorm-hibernate5/src/test/groovy/grails/gorm/tests/dirtychecking/HibernateDirtyCheckingSpec.groovy @@ -4,7 +4,6 @@ import grails.gorm.annotation.Entity import grails.gorm.dirty.checking.DirtyCheck import grails.gorm.transactions.Rollback import org.grails.orm.hibernate.HibernateDatastore -import org.springframework.transaction.PlatformTransactionManager import spock.lang.AutoCleanup import spock.lang.Issue import spock.lang.Shared @@ -67,6 +66,12 @@ class HibernateDirtyCheckingSpec extends Specification { when: person.save(flush:true) + + then: + !person.address.hasChanged() + person.address.listDirtyPropertyNames().isEmpty() + + when: hibernateDatastore.sessionFactory.currentSession.clear() person = Person.first()