I have a grails domain object that I validate and then save as such:
if(foo.hasErrors()) {
transactionStatus.setRollbackOnly()
respond foo.errors, view: 'create'
return
}
foo.save flush:true, failOnError:true
println(foo)
There are no errors given on the save. But when I call the println, it says my object is unsaved. However, if I check the database, it has indeed been persisted. Is there something I can check to tell me why grails is telling me it is unsaved. The grails version is 3.0.9.
After more testing, it looks like it is related to the way I specify the key in my domain mapping.
static mapping = {
version false
autoTimestamp false
id name:'foo_id', generator:'increment'
}
If I remove the id field and let Grails handle it by default the object is saved properly. If I add the id field back, it has the issue described above. Is there a reason for it?
I wouldn't be too concerned about the format of toString(). You're calling save with failOnError: true, so it's safe to assume that an exception would be thrown if persistence fails. If you don't specify this argument, you can check that persistence succeeds like so
if (foo.save()) {
println 'it worked'
} else {
println 'if failed'
}
try
foo = foo.save flush:true, failOnError:true
println(foo)
Related
When I do custom rejectValue in a service method grails loses that error(s) between service method and return to controller. This seems to happen when updating a row instance, but not when creating one.
In service
def specialValidation(petInstance){
if(petInstance.petType.requiresStateId && !petInstance.StateId){
petInstance.errors.rejectValue('StateId','StateId required');
}
println petInstance.errors //shows 1 error
return petInstance;
}
In controller
...
petInstance.properties=params;
petInstance=petService.specialValidation(petInstance);
println petInstance.errors //shows 0 errors
How is the error being lost when the instance changes hands from service to controller?
It can be because of transactional service. Service opens separate transaction for each method and clears entities after method end. You can find this mentioned in docs(read the last paragraph of part )
I had the same problem. Than I've added NotTransactional annotation to validation method, and it helped. Errors were saved.
Well I did something simular :
orderService.validate(order, params)
if (order.hasErrors()) {
return render(view: 'create', model: [order: order])
}
In the Service I do some validation like this:
if (end.before(start)) {
order.errors.rejectValue("end", '', 'ERROR');
}
The different to yours is that i didn't set the errorCode but the message at itself, have a look at the rejectValue Methods:
void rejectValue(String field, String errorCode);
void rejectValue(String field, String errorCode, String defaultMessage);
You could also try to use the rejectValue method like me, maybe it helps.
I found you can also avoid this by using
MyDomain.read(id)
instead of
MyDomain.get(id)
tl:dr; This is a bit involved of a problem, any advice is welcome, appreciate reading in advance :)
My coworkers and I have been struggling a bit with an odd behavior in our batch processing application. We recently upgraded it from Grails 1.3.7 to 2.1
The stacktrace is showing the following error:
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException:
Cannot insert the value NULL into column 'date_created',
table 'dev.dbo.notification_log'; column does not allow nulls. INSERT fails.
...
[quartzScheduler_Worker-1] [||] ERROR hibernate.AssertionFailure - an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
org.hibernate.AssertionFailure: null id in com.virtuwell.domain.NotificationLog entry (don't flush the Session after an exception occurs)
at org.quartz.core.QuartzScheduler.notifyJobListenersWasExecuted(QuartzScheduler.java:1891)
at org.quartz.core.JobRunShell.notifyJobListenersComplete(JobRunShell.java:352)
at org.quartz.core.JobRunShell.run(JobRunShell.java:223)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:546)
[quartzScheduler_Worker-1] [||] ERROR listeners.SessionBinderJobListener - Cannot flush Hibernate Sesssion, error will be ignored
org.hibernate.AssertionFailure: null id in com.virtuwell.domain.NotificationLog entry (don't flush the Session after an exception occurs)
at org.quartz.core.QuartzScheduler.notifyJobListenersWasExecuted(QuartzScheduler.java:1891)
at org.quartz.core.JobRunShell.notifyJobListenersComplete(JobRunShell.java:352)
at org.quartz.core.JobRunShell.run(JobRunShell.java:223)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:546)
Here is the code of that particular Domain Object (NotificationLog)
class NotificationLog implements Serializable{
Date dateCreated
Notification notification
NotificationDeliveryState deliveryState
String message
static mapping = {
message type: 'text'
}
}
What's strange, however, is this error doesn't occur EVERY time that domain object is persisted, and we only have one place in the code that object is ever persisted, shown below:
class NotificationLogService {
boolean transactional = true
def logNotification(Notification notification, message, deliveryState) {
def notificationLog = new NotificationLog(
notification: notification,
deliveryState: deliveryState,
message:message
)
try{
notificationLog.save(failOnError:true)
} catch (Exception e) { // Failure to save a notificationLog should not rollback the calling transaction
log.error "NotificationLog State:[$deliveryState] for notification:${notification?.id} did not save. Errors: ${notificationLog?.errors}, Message:$message", e
}
}
}
We've found a 'hack' of a workaround in the below SO question where we are no longer periodically seeing the error in the logs, by adding this to the Domain Object
static mapping = {
autoTimestamp true
}
But this isn't the only domain we're seeing with the SAME periodic failure to save (thus, I need to add the mapping to other domains), and if this truly is necessary for dateCreated to function properly in Grails 2.1, I need to add it to a LOT more domains!
Worse, I can't reproduce it in a Unit or Integration test, its only happening on our running Dev and QA instances.
So, 2 Questions:
Does anyone know why this error might be periodically occurring?
If not, is there a way I can globally add this autoTimestamp true mapping to ALL of my project's domain objects (I've been unable to find documentation for how to add it at all, other than to set it to false)
Relevant SO Question:
dateCreated, lastUpdated fields in Grails 2.0
Relevant Grails Maillist discussion
http://grails.1312388.n4.nabble.com/dateCreated-lastUpdated-in-Grails-2-0-td4337894.html
Relevant Grails docs on autoTimestamp GORM properties
http://grails.org/doc/latest/guide/GORM.html#eventsAutoTimestamping
To answer both of the questions:
EDIT Try flush: true while save otherwise autoTimestamp true is the last resort. I have not researched to find out the cause of this issue.
You can set this property in Config.groovy to make it applicable for all domain classes.
grails.gorm.default.mapping = {
autoTimestamp true //or false based on your need
}
Have you tried to manually insert the date when creating a new NotificationLog?
Like this:
class NotificationLogService {
boolean transactional = true
def logNotification(Notification notification, message, deliveryState) {
def notificationLog = new NotificationLog(
dateCreated: new Date(),
notification: notification,
deliveryState: deliveryState,
message:message
)
try{
notificationLog.save(failOnError:true)
} catch (Exception e) { // Failure to save a notificationLog should not rollback the calling transaction
log.error "NotificationLog State:[$deliveryState] for notification:${notification?.id} did not save. Errors: ${notificationLog?.errors}, Message:$message", e
}
}
}
On a Grails 2.1.0 I am trying to dynamically updating a field on a domain class. The object gets binded and it looks fine, until the save method is called, which throws the following exception:
java.lang.IllegalStateException: Cannot make an immutable entity modifiable.
try {
def bindParams = [:]
bindParams."$paramsFieldName" = "$paramsValue"
def domainClass = grailsApplication.domainClasses.find { it.clazz.simpleName == paramsDomain }.clazz
def objectInstance = domainClass.findById(paramsId)
objectInstance."$paramsFieldName" = "$paramsValue"
bindData(objectInstance, bindParams)
objectInstance.save(flush:true ,failOnError:false)
return objectInstance
}
catch (Exception ex) {
log.error ex
return null
}
I tried to bind the field using direct assigment and worked well.
objectInstance."$paramsFieldName" = convertToType( fieldType.name,paramsValue)
but then I need to handle the type conversion for each case (I assume). What I need is the BindDynamicMethod handles the binding for me. What happens to the object when binding it using the BindDynamicMethod that makes is immutable?. Or what am I doing wrong that is causing it?
=========================================================
PARTIALLY SOLVED
It turned out that this was happening on some of the domains, but some that were using cache on their mapping was throwing this exception.
class UploadSettings {
String profile = "default"
String displayName
String name
String value
String defaultValue
static mapping = {
//cache usage:'read-only'
}
}
So I guess now my question is if a domain is using cache , why cant we update its value? Or how can we do that? Is there a way to capture if the domain is immutable?
Thanks
Yes by setting it to read-only you are making the object immutable as the error says, IMHO this is misleading as we are in the context of caching but there is some rationale behind this.
If you need caching at the domain level then setting it to read-write should do the trick
See cache usages
I am trying to store an instance or at least id of each domain that is auditable. For example, there is a domain 'Student' and is auditable. So, whenever the domain an instance of domain is created or edited, a new record is stored in audit log. What I want is to store the id of each domain that is saved or updated. How can it be done ?
Thanks in advance
You can use Grails Envers plugin that uses Hibernate Envers underneath. Plugin information can be found on plugins site here.
You can better refer to the url: http://grails.org/plugin/audit-logging
The above plugin works well for the domain class without collection, in case if you using collections in domain class, refer this stackoverflow link: How to determine Collection changes in a Hibernate PostUpdateEventListener?
Hope it will help a lot
I Wouldn't use the audit plugin , it has some issues when running your tests. This plugin in was created when the GORM didn't provide support for events. You can intercept the following events
beforeInsert - Executed before an object is initially persisted to the database
beforeUpdate - Executed before an object is updated
beforeDelete - Executed before an object is deleted
beforeValidate - Executed before an object is validated
afterInsert - Executed after an object is persisted to the database
afterUpdate - Executed after an object has been updated
afterDelete - Executed after an object has been deleted
onLoad - Executed when an object is loaded from the database
Hope this helps
You can use events with AuditLog plugin like below and then add what ever you would like too. Hope this makes sense
def onChange = { oldMap,newMap ->
println "Person was changed ..."
oldMap.each({ key, oldVal ->
if(oldVal != newMap[key]) {
println " * $key changed from $oldVal to " + newMap[key]
}
})
}
def onSave = {
println "new datainserted"
// may optionally refer to newState map
}
In any of these methods we can use
def event = new AuditLogEvent(
actor: actor,
className: className,
eventName: eventName,
persistedObjectId: objectId,
propertyName: propertyName,
newValue: newValue
)
if (event.validate()) {
auditLogListener.saveAuditLog(event)
log.info "Logged audit event [$event]"
}
Updated post:
In a Controller if I do this:
def obj = new Test(name:"lol")
obj.save(flush:true)
obj.name = "lol2"
//a singleton service with nothing to do with obj
testService.dostuff()
/*
"obj" gets persisted to the database right here
even before the next println
*/
println "done"
Can anyone please explain me why is this happening with Grails 1.3.7 and not with Grails 2? What is the reason?
I know I could use discard() and basically restructure the code but I am interested in what and why is happening behind the scenes. Thanks!
Old post:
I have a test Grails application. I have one domain class test.Test:
package test
class Test {
String name
static constraints = {}
}
Also I have a service test.TestService:
package test
class TestService {
static scope = "singleton"
static transactional = true
def dostuff() {
println "test service was called"
}
}
And one controller test.TestController:
package test
class TestController {
def testService
def index = {
def obj = new Test(name:"lol")
obj.save(flush:true)
obj.name = "lol2"
testService.dostuff()
println "done"
}
}
So what I do:
Create a domain object
Change one of it's properties
Call a singleton service method
What I would expect:
Nothing gets persisted to the db unless I call obj.save()
What happens instead:
Right after the service call Grails will do an update query to the database.
I have tried the following configuration from this url: http://grails.1312388.n4.nabble.com/Turn-off-autosave-in-gorm-td1378113.html
hibernate.flush.mode="manual"
But it didn't help.
I have tested it with Grails 1.3.7, Grails 2.0.3 does not have this issue.
Could anyone please give me a bit more information on what is exactly going on? It seems like the current session has to be terminated because of the service call and because the object is dirty it is getting automatically persisted to the database after the service call. What I don't understand that even with the manual flush mode configuration in Hibernate does not help.
Thanks in advance!
I'm not sure what about that thread you linked to made you think it would work. They all said it wouldn't work, the ticket created has been closed as won't fix. The solution here is to use discard() as the thread stated.