I am not sure if this is a bug or the intended behaviour but I am quite
sure the documentation does not talk about it. @Walter can you please give
it a look?

I've used AI to summarize the findings.


*GORM/Hibernate does not detect changes to inherited properties during
dirty checking.*

The property is correctly persisted if another property of the entity is
modified at the same time, or if markDirty() is explicitly called. However,
changing only the inherited property does not result in an UPDATE being
issued.
Expected behavior

Changing a persistent property should mark the entity as dirty and cause
the property to be persisted when calling:

obj.save(flush: true)

Actual behavior

The entity is not considered dirty when only an inherited property is
changed.

For example:

TPaymentMethod obj = TPaymentMethod.get(id)

obj.dateDisabled = LocalDateTime.now()
obj.usernameDisabled = 'TEST'

println obj.isDirty()
println obj.isDirty('dateDisabled')
println obj.isDirty('usernameDisabled')

obj.save(flush: true, failOnError: true)

The output is:

dirty=false
dirty date=false
dirty username=false

No SQL UPDATE is generated and the values are not persisted.

However, if another property declared directly on TPaymentMethod is changed:

obj.description = obj.description + ' TEST'
obj.dateDisabled = LocalDateTime.now()

obj.save(flush: true, failOnError: true)

the entity is detected as dirty and both changes are persisted correctly.

Explicitly marking the entity as dirty also works:

obj.dateDisabled = LocalDateTime.now()
obj.usernameDisabled = 'TEST'

obj.markDirty()

obj.save(flush: true, failOnError: true)

Minimal exampleBase class

abstract class SystemManaged {

    Long id
    Long version

    LocalDateTime dateCreated
    LocalDateTime lastUpdated
    LocalDateTime dateDeleted
    LocalDateTime dateDisabled

    String usernameCreated
    String usernameUpdated
    String usernameDeleted
    String usernameDisabled

    static constraints = {
        id nullable: true
        version nullable: true

        dateCreated nullable: true
        lastUpdated nullable: true
        dateDeleted nullable: true
        dateDisabled nullable: true

        usernameCreated nullable: true
        usernameUpdated nullable: true
        usernameDeleted nullable: true
        usernameDisabled nullable: true
    }
}

Domain class

@GrailsCompileStatic
class TPaymentMethod extends SystemManaged
        implements GormEntity, MultiTenant<TPaymentMethod> {

    String description

    static constraints = {
        description nullable: false
    }
}

Reproduction

@Transactional
void test(Long id) {
    TPaymentMethod obj = TPaymentMethod.get(id)

    obj.dateDisabled = LocalDateTime.now()
    obj.usernameDisabled = 'TEST'

    println "dirty=${obj.isDirty()}"
    println "dirty dateDisabled=${obj.isDirty('dateDisabled')}"
    println "dirty usernameDisabled=${obj.isDirty('usernameDisabled')}"
    println "dirty properties=${obj.dirtyPropertyNames}"

    obj.save(flush: true, failOnError: true)
}

Observed result:

dirty=false
dirty dateDisabled=false
dirty usernameDisabled=false
dirty properties=[]

No UPDATE is generated.

But this works:

obj.description = 'changed'
obj.dateDisabled = LocalDateTime.now()

obj.save(flush: true, failOnError: true)

And this also works:

obj.dateDisabled = LocalDateTime.now()
obj.markDirty()

obj.save(flush: true, failOnError: true)

Gianluca Sartori
--
https://dueuno.com

Reply via email to