Autotimestamping requires explicitly initializing its properties?

304 views
Skip to first unread message

Ian Tegebo

unread,
Jun 10, 2015, 6:50:02 PM6/10/15
to gra...@googlegroups.com
I added "dateCreated" and "lastUpdated" Date properties to a domain class and then got a PropertyValueException after trying to save it.  Further, one can't pass in values in the constructor because apparently these properties are now no longer bindable:


So, IIUC, one must instantiate a new domain object and then explicitly set these properties before saving:

myObj = new MyObj(someProp: 'foo')
myObj.dateCreated = new Date()
myObj.lastUpdated = new Date()
myObj.save()

Clearly, I don't want to have to remember to do that all the time.  A no-args constructor would seem to be the next obvious choice:

MyObj() { dateCreated = new Date(); lastUpdated = new Date() }

But then I expect many unnecessary calls to Date as they'll be overwritten by incoming values from the db; plus, I'll need to remember to add such a no-args constructor to each domain class.  What am I doing wrong?

I would have expected autoTimestamp to automatically timestamp my domain objects.

The documentation I've read:


However, now that I read more of that last reference, I see that the recommendation might be to set static NULL_DATE and then add a "beforeInsert" event that sets the properties if they've not already been set.  Unfortunately, that's even more boilerplate to remember though it is an improvement over the no-args constructor as it avoids unnecessarily calling Date.



Ian Tegebo

unread,
Jun 10, 2015, 11:34:14 PM6/10/15
to gra...@googlegroups.com
It appears that the important thing is that the properties are not null.  In this case, I've chosen to use a trait:

trait Timestamped {
    private static final Date NULL_DATE = new Date(0)
    Date dateCreated = NULL_DATE
    Date lastUpdated = NULL_DATE
}

So far, it works with ad hoc testing from the console.

Aleh Bykhavets

unread,
Jun 11, 2015, 4:52:11 AM6/11/15
to gra...@googlegroups.com
Ian,

I've tried simple test app at 2.5.0 and 2.4.5 -- auto time stamping definitely works.
Could you provide that domain class, please?

Possible issues:
1) At domain class
class Zet {
    String message
    Date dateCreated
    def lastUpdated // !!! MUST define `Date` type

    static constraints = {
      dateCreated(nullable: false)  // !!! <- tempting, but will not work
    }
}

2) autoTimestamp is false at domain or in Config.groovy
3) BTW, at unit tests auto time stamping does not work
4) maybe using other plugins or other versions of some plugins compared to current version of Grails.


Thanks

Ian Tegebo

unread,
Jun 11, 2015, 3:45:53 PM6/11/15
to gra...@googlegroups.com
I should have clarified in my initial message that I was experimenting from "grails console" on 2.5.0.

I had a domain class like:

class Foo {
  Date dateCreated
  Date lastUpdated

  String bar
}

and then in "grails console" I'd do:

foo = new Foo(bar: "baz")
foo.save(flush:true) // property, or some hibernate dao exception 

I found that both date properties needed to be initialized (and not using constructor parameters) in order not to throw exceptions on .save().  Also, apparently it's necessary to explicitly call "new BootStrap().init(ctx.servletContext)" but only after something like "import my.app.package.*"; was that always true or was there a time when one didn't need to do that to call GORM methods from the console?  I ask because Peter Lodbrock in 2010 had mentioned the console as a great tool for experimenting with domain classes:


Also, Beckwith's 2013 "Programming Grails" mentions the same technique but does not make reference to needing to call BootStrap first.  Lastly, even the "Quickstart" section of the 2.5.0 User Guide for GORM mentions using the console but doesn't make any reference to needing to call BootStrap:

Are the consoleshell still good places to experiment with GORM?

Owen Rubel

unread,
Jun 16, 2015, 6:27:04 PM6/16/15
to gra...@googlegroups.com
if you want a default in the Domain, you build your domain class that way:

class Foo {
  Date dateCreated = new Date()
  Date lastUpdated = new Date()
  ...
}

Thus the defaults will be set on initial insert. You will however have to change the lastUpdated field with every insert naturally
Reply all
Reply to author
Forward
0 new messages