Is there any way I can override the value of dateCreated
field in my domain class without turning off auto timestamping?
I need to test controller and I ha
As of Grails 3 and GORM 6 you can tap into AutoTimestampEventListener
to execute a Runnable
that temporarily ignores all or select timestamps.
The following is a small snippet I use in my integration tests where this is necessary:
void executeWithoutTimestamps(Class domainClass, Closure closure){
ApplicationContext applicationContext = Holders.findApplicationContext()
HibernateDatastore mainBean = applicationContext.getBean(HibernateDatastore)
AutoTimestampEventListener listener = mainBean.getAutoTimestampEventListener()
listener.withoutTimestamps(domainClass, closure)
}
Then in your case you could do the following:
executeWithoutTimestamps(BlogMessage, {
Date someValidDate = new Date() - (20*365)
BlogMessage message = new BlogMessage()
message.dateCreated = someValidDate
message.save(flush: true)
})
Getting a hold of the ClosureEventListener allows you to temporarily disable grails timestamping.
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
import org.codehaus.groovy.grails.commons.spring.GrailsWebApplicationContext
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration
import org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventTriggeringInterceptor
import org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventListener
class FluxCapacitorController {
def backToFuture = {
changeTimestamping(new Message(), false)
Message m = new Message()
m.dateCreated = new Date("11/5/1955")
m.save(failOnError: true)
changeTimestamping(new Message(), true)
}
private void changeTimestamping(Object domainObjectInstance, boolean shouldTimestamp) {
GrailsWebApplicationContext applicationContext = servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
GrailsAnnotationConfiguration configuration = applicationContext.getBean("&sessionFactory").configuration
ClosureEventTriggeringInterceptor interceptor = configuration.getEventListeners().saveOrUpdateEventListeners[0]
ClosureEventListener listener = interceptor.findEventListener(domainObjectInstance)
listener.shouldTimestamp = shouldTimestamp
}
}
There may be an easier way to get the applicationContext or Hibernate configuration but that worked for me when running the app. It does not work in an integration test, if anyone figures out how to do that let me know.
Update
For Grails 2 use eventTriggeringInterceptor
private void changeTimestamping(Object domainObjectInstance, boolean shouldTimestamp) {
GrailsWebApplicationContext applicationContext = servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
ClosureEventTriggeringInterceptor closureInterceptor = applicationContext.getBean("eventTriggeringInterceptor")
HibernateDatastore datastore = closureInterceptor.datastores.values().iterator().next()
EventTriggeringInterceptor interceptor = datastore.getEventTriggeringInterceptor()
ClosureEventListener listener = interceptor.findEventListener(domainObjectInstance)
listener.shouldTimestamp = shouldTimestamp
}
You can try to disable it by setting autoTimestamp = false
in the domain class mapping. I doubt about global overriding because the value is taken directly from System.currentTimeMillis()
(I'm looking at org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventListener.java).
So I can only suggest that you override a setter for dateCreated
field in your class, and assign your own value. Maybe even metaclass access will work, like
Date stubDateCreated
...
myDomainClass.metaClass.setDateCreated =
{ Date d -> delegate.@dateCreated = stubDateCreated }
I'm using something like this for an initial import/migration.
Taking gabe's post as a starter (which didn't work for me Grails 2.0), and looking at the old source code for ClosureEventTriggeringInterceptor in Grails 1.3.7, I came up with this:
class BootStrap {
private void changeTimestamping(Object domainObjectInstance, boolean shouldTimestamp) {
Mapping m = GrailsDomainBinder.getMapping(domainObjectInstance.getClass())
m.autoTimestamp = shouldTimestamp
}
def init = { servletContext ->
changeTimestamping(new Message(), false)
def fooMessage = new Message()
fooMessage.dateCreated = new Date("11/5/1955")
fooMessage.lastUpdated = new Date()
fooMessage.save(failOnError, true)
changeTimestamping(new Message(), true)
}
}