1

After modifying an inherited many-to-one relationship field the change cannot be detected using isDirty() or similar checks. How can I detect the change without manually checking the field?

I have already attempted to use @DirtyCheck, however this only works for simple fields, such as lookupId. @DirtyCheck does not fix relationship fields, such as lookup.

Utilizing:

  • GORM without grails, specifically gorm-hibernate5-spring-boot:6.1.9.RELEASE.
  • Groovy 2.4.15
  • Java 8u151

The following test is passing and displays the current undesired behavior.

@Unroll
def 'MyObject dirty check with setter: #setter'() {
    given: '2 existing Lookups and a newly created MyObject'
        Lookup lookup1 = new Lookup(
            code: 'CODE1'
        ).save(flush: true, failOnError: true)
        Lookup lookup2 = new Lookup(
            code: 'CODE2'
        ).save(flush: true, failOnError: true)
        MyObject myObject = new MyObject(
            name: 'someName',
            lookup: Lookup.get('CODE1'),
        ).save(flush: true, failOnError: true)
    when: 'change the lookup'
        if(setter) {
            myObject.setLookup(Lookup.get('CODE2'))
        } else {
            myObject.lookup = Lookup.get('CODE2')
        }
    then: 'we can manually detect the change'
        myObject.lookup.code == 'CODE2'
        myObject.getPersistentValue('lookup').code == 'CODE1'
        myObject.getPersistentValue('lookup') != myObject.lookup
        myObject.getPersistentValue('lookup').code != myObject.lookup.code
    and: 'dirty checks do not detect the change'
        false == (myObject.lookup.getDirtyPropertyNames() ||
            myObject.getDirtyPropertyNames() ||
            myObject.lookup.isDirty() ||
            myObject.lookup.hasChanged() ||
            myObject.hasChanged() ||
            myObject.isDirty() ||
            ((LookupBase)myObject.lookup).isDirty() ||
            ((LookupBase)myObject.lookup).hasChanged() ||
            ((ModelBase)myObject).isDirty() ||
            ((ModelBase)myObject).hasChanged()
        )
    where:
        setter << [false, true]
}

There is a parent class:

@ToString(includeNames=true)
@EqualsAndHashCode(
    includes = [ 'lookup', 'lookupId', ]
)
@AutoClone
class ModelBase {
    UUID id

    Lookup lookup
    String lookupId
}

And another parent class:

@ToString(includeNames=true)
@EqualsAndHashCode(
    includes = [ 'code', ]
)
class LookupBase {
    String code

    static constraints = {
        code nullable: false
    }

    static mapping = {
        id name: 'code', generator: 'assigned'
    }
}

A child class:

@Entity
@ToString(
    includeNames=true,
    includeSuperProperties=true,
    excludes = [ ... ]
)
@EqualsAndHashCode(
    callSuper = true,
    excludes = [ ... ]
)
class MyObject extends ModelBase implements GormEntity<MyObject> {
    String name
}

And another child class:

@Entity
@ToString(includeNames=true, includeSuperProperties=true)
@AutoClone
class Lookup extends LookupBase implements GormEntity<Lookup> {
    static constraints = {
        code maxSize: 20
    }
}
bigfatony
  • 63
  • 5
  • If, as you say, "adding DirtyCheck" doesn't work for the lookup field, but works for the lookupId field, I would suggest that you also need to add DirtyCheck to the base class(es) of Lookup. – billjamesdev Jul 20 '18 at 21:22

2 Answers2

0

Without digging into your code too much, have you tried adding @DirtyCheck to either the parent, child or both class definitions? I have seen similar behavior when a Domain class inherited from a non Domain (src/main/groovy) class where Dirty Checking had to be explicitly added to the parent class.

Edit: sorry, didn't see the end of your post where you said you'd already tried this.

Trebla
  • 1,164
  • 1
  • 13
  • 28
0

We found this same issue, especially when inheriting from abstract classes. It was necessary to put the @DirtyCheck annotation on the parent class, or the class containing fields that we wanted considered in determining what has changed and needs writing to the DB.

https://gorm.grails.org/6.0.x/api/grails/gorm/dirty/checking/DirtyCheck.html

Possible duplicate: Grails 3.1.1 - Dirty checking not working when model class extends another groovy class

David Ferenczy Rogožan
  • 23,966
  • 9
  • 79
  • 68
billjamesdev
  • 14,554
  • 6
  • 53
  • 76
  • Are there sample code snippets that we can look at? I'm in the middle of investigating this myself. – Kevin Tan Dec 27 '18 at 17:18
  • @KevinTan Sorry, my code isn't publicly accessible. Basically, it's `abstract class A` with field `afield` extended by `class B extends A`. If you load an instance of class B, but only change the field `afield`, then save the instance, it won't save anything, since it won't see that the class B instance is dirty. You have to annotate the `class A` class with @DirtyCheck to get it to work. – billjamesdev Dec 27 '18 at 22:02