Search code examples
grailsgrails-orm

Grails boolean field not saved when updated


I have a model with two classes A & B, both of them have a "boolean lastVersion" field.

Class "A" has an association "B b", and on A.beforeInsert/beforeUpdate the value of A.lastVersion is copied into B.lastVersion.

The default value for A.lastVersion and B.lastVersion is true. When I change a.lastVersion to false and do a.save(), neither lastVersion is set to false. If I do a.save(flush:true) only the a.b.lastVersion is saved as false;

Any ideas of what's the issue here?

I've tested this on v2.1.0 and v2.3.7 using the H2 database. Edit: tested on MySQL, got the same behavior.

Here you can find both sample applications (code also included below). The weird behavior happens when running the apps and checking on the H2 dbconsole. There is a unit test called VersionTests that also gets an inconsistent behavior IMO.

package testbools
class Version {

    static constraints = {
        ci (nullable: true)
    }

    boolean lastVersion = true
    CompositionIndex ci

    def beforeInsert() {
      this.ci.lastVersion = this.lastVersion
   }
   def beforeUpdate() {
      this.ci.lastVersion = this.lastVersion
   }
}



package testbools 
class CompositionIndex {

    static constraints = {
    }

    boolean lastVersion = true

    static belongsTo = [Version]
}

And the test:

package testbools 
import grails.test.mixin.*
import org.junit.*

/**
 * See the API for {@link grails.test.mixin.domain.DomainClassUnitTestMixin} for usage instructions
 */
@TestFor(Version)
class VersionTests {

    void testSomething() {

       def v = new Version(ci: new CompositionIndex())
       if (!v.save()) println v.errors

       def vget = Version.get(1)
       assert vget.lastVersion
       assert vget.ci.lastVersion

       // change value
       vget.lastVersion = false
       if (!vget.save()) println vget.errors


       // value has been changed?
       assert !vget.lastVersion
       assert !vget.ci.lastVersion


       // value has been stored?
       def vget2 = Version.get(1)
       assert !vget2.lastVersion
       assert !vget2.ci.lastVersion
    }
}

Solution

  • I've added your source code from the testbools237 file to make it easier for others to review the source code, but I haven't got sufficient reputation to be allowed to approve the edit, so perhaps either the original poster or someone else can review and update please.

    You're creating a new CompositionIndex within your unit test, but it's not mocked out, so I don't think your test will work as expected. I would try creating a collaborator by putting:

    @Mock(CompositionIndex)
    

    under this annotation:

    @TestFor(Version)
    

    Details here: http://grails.github.io/grails-doc/2.4.3/guide/testing.html#unitTesting - see the section entitled Test Mixin Basics.

    EDIT:

    Further to my last, you also need to ensure that the domain model you're editing has actually been saved to the database before making your test assertion. Calling save() on a domain object doesn't actually mean that the object is saved, it simply means that you've said you want to save it. Peter Ledbrook explains it very well here.

    If you change your save just before your failing test assertion from:

    if (!vget.save()) println vget.errors
    

    to this:

    if (!vget.save(flush: true)) println vget.errors
    

    then your test should behave as expected (your failing test now passes on my machine, at any rate).