Search code examples
grailsgrails-ormgrails-2.0grails-domain-class

Why don't unflushed domain objects revert to their "saved" state after discard? Can I get the "clean" version?


Confused here about domain objects in intermediate states.

So here's a class:

class Foo {
    Double price
    String name
    Date creationDate = new Date()

    static constraints = {
        price min: 50D
        creationDate nullable: false
    }
}

And this test case:

@TestFor(Foo)
class FooSpec extends Specification {
   void "test creation and constraints"() {
        when: "I create a Foo within constraints"
            Foo f= new Foo(price: 60D, name: "FooBar")
        then:  "It validates and saves"
            f.validate()
            f.save()
        when: "I make that foo invalid"
            f.price=49D
        then: "It no longer validates"
            !f.validate()
        when: "I discard the changes since the last save"
            f.discard()
        then: "it validates again"
            f.validate()                //TEST FAILS HERE
    }
}

So this test fails on the last validate, because f.discard() doesn't seem to revert f to its "saved" state. Notes on the grails docs appear to indcate this is intended (alothough very confusing to me at least) behavior. That all "discard" does is mark it as not to be persisted (which it wouldn't be anyway if it fails the constraints, so in a case like this, I guess it does nothing right? ) I thought refresh might get me the saved state, or even Foo.get([id]) but none of that works. Foo.get gets the corrupted version and f.refresh throws an nullPointer apparently because creationDate is null and it shouldn't be.

So all that is confusing, and seems like there's no way to get back to saved state of the object if it hasn't been flushed. Is that really true? Which sort of raises the question "In what respect is the object "saved?"

That would seem to mean I would want to be making sure the I'm definitely reading from the db not the cache if I want to be sure I'm getting a valid persisted state of the object, \not some possibly corrupted intermediate state.

A related question -- Not sure the scope of the local cache either -- is it restricted to the session? So if I start another session and retrive that id, I'll get a null because it was never saved, not the corrupt version with the invalid price?

Would appreciate someone explaining the underlying design principal here.
Thanks.


Solution

  • Hibernate doesn't have a process for reverting state, although it seems like it could since it keeps a clean copy of the data for dirty checking. Calling the GORM discard() method calls a few layer methods along the way but the real work is done in Hibernate's SessionImpl.evict(Object) method, and that just removes state from the session caches and disassociates the instance from the session, but it does nothing to the persistent properties in the instance. If you want to undo changes you should evict the instance with discard() and reload it, e.g. f = Foo.get(f.id)