Quartz job for Grails cannot update record - grails

I have following Quartz job in my Grails application:
class DataWarehouseJob {
def execute(context) {
LeadList list = context.mergedJobDataMap.get('list')
if (list.processing) {
return
}
list.processing = true
list.save(failOnError: true)
...
}
}
list is passed as a parameter on job triggerNow call and it's populated with correct values. This list already exists in database. However, when it tries to save the list, following error occurs:
org.quartz.JobExecutionException: org.springframework.dao.DuplicateKeyException: A different object with the same identifier value was already associated with the session : [haystack.LeadList#169]; nested exception is org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session : [haystack.LeadList#169]
at grails.plugins.quartz.GrailsJobFactory$GrailsJob.execute(GrailsJobFactory.java:111)
at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
Caused by: org.springframework.dao.DuplicateKeyException: A different object with the same identifier value was already associated with the session : [haystack.LeadList#169]; nested exception is org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session : [haystack.LeadList#169]
...
Caused by: org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session : [haystack.LeadList#169]
at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:618)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:301)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:244)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:109)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90
...
I have tried adding flush: true as parameter to save function, calling merge on list before save, annotating class with #ExecuteInJTATransaction, saving LeadList withNewSession, but nothing helped - same error always occurs on same line (list.save)
The weirdest thing is that this code worked before and I'm not sure which change could break it - there were no commits of changes touching this class in quite some time.
The code is written in Grails 3.1.5 and running on Tomcat 8.

I don't wanna delete the question, since solution might benefit someone.
The problem was because job was accepting the LeadList object and I guess it created the copy of it instead of passing the object by reference, and that's what caused the problem.
Solution is to send ID of the object as a parameter to job instead of the whole object, and in job to first fetch object by ID before making changes in database:
// LeadList list = context.mergedJobDataMap.get('list')
long listId = context.mergedJobDataMap.get('listId')
LeadList list = LeadList.findById(listId)
Still, I have no idea how passing the whole object worked before and then at some point started causing the issue all of a sudden.

Related

Puzzling validation error after upgrading from Grails 2.2.5 to 4.0.11

A method which was working for a long time in Grails 2.2.5 has broken after moving to 4.0.11 with a validation error on saving, and the error is a puzzle to me. I have a domain class 'Decline' which has one of its properties 'user', which is of domain class user. As part of the save process I assign the currently logged in user to this property:
Decline decline = new Decline()
decline.policy = policy
decline.declineTime = new Date()
decline.field = field
decline.cause = reason
decline.user = User.getUser()
decline.save(flush:true)
This was working fine in 2.2.5 but now I get the following validation error:
Field error in object 'myapp.pei.Decline' on field 'user.userType': rejected value [DIRECT_CLIENT]; codes [myapp.User.userType.nullable.error.myapp.pei.Decline.user.userType,myapp.User.userType.nullable.error.user.userType,myapp.User.userType.nullable.error.userType,myapp.User.userType.nullable.error.myapp.UserType,myapp.User.userType.nullable.error,user.userType.nullable.error.myapp.pei.Decline.user.userType,user.userType.nullable.error.user.userType,user.userType.nullable.error.userType,user.userType.nullable.error.myapp.UserType,user.userType.nullable.error,myapp.User.userType.nullable.myapp.pei.Decline.user.userType,myapp.User.userType.nullable.user.userType,myapp.User.userType.nullable.userType,myapp.User.userType.nullable.myapp.UserType,myapp.User.userType.nullable,user.userType.nullable.myapp.pei.Decline.user.userType,user.userType.nullable.user.userType,user.userType.nullable.userType,user.userType.nullable.myapp.UserType,user.userType.nullable,nullable.myapp.pei.Decline.user.userType,nullable.user.userType,nullable.userType,nullable.myapp.UserType,nullable]; arguments [userType,class myapp.User]; default message [Property [{0}] of class [{1}] cannot be null]
There are two things which are puzzling about this. Firstly, and more importantly, this appears to be an error saving the User object. But why is it even trying to save the User object? I have assigned an existing User object which it should be using. Secondly, the specific error is 'rejected value [DIRECT_CLIENT]' for field 'user.userType', but the error message is that this field cannot be null. So it's rejecting a value but telling me it cannot be null! The value, incidentally, is of a UserType enum defined thus:
public enum UserType {
ADMIN_USER,ADMIN_OWNER_USER,SUPER_USER,BROKER,DIRECT_CLIENT
}
I wonder what change from version 2.2.5 to 4 (or maybe 3) could have caused this?
It seems there was some change in behaviour of deepValidate between Grails 2.x and 4.x which is causing this, although I don't see why validation of the associated User object should be failing when it can actually be saved OK separately. But what got me past this issue was to set the following in the mapping block for Decline:
user cascadeValidate: 'none'
This ensures that when the Decline object is saved it does not attempt to validate the User as well.

In GORM, Object is not modified in database when the object property is modified at Controller

In my Grails App, I have bootstrapped an object of a domain class as:
def user1 = new Users(userID: 1, name: "John Doe")
user1.save()
In my dashboard controller i have retrieved the object and modified its property name as:
Users userObj = Users.get((Long) 1)
println(userObj as JSON); // This gives me: {"class":"com.prabin.domains.Users","id":1,"name":"John Doe"}
userObj.name = "anonymous"
Now i create a new Users object to retrieve the changed object with same ID 1 as
Users otherUserObj = Users.get((Long) 1) // **Line 2** Is this retrieving from database or from GORM session?
print(otherUserObj as JSON)// This gives me: {"class":"com.prabin.domains.Users","id":1,"name":"anonymous"}
But the value of object in database is not changed. And even when i retrieve the Users object of same id 1 in another controller it gives me the initial object rather than the modified as:
Users userObjAtDifferentController = Users.get(1);
print(userObjAtDifferentController) //This gives me: {"class":"com.prabin.domains.Users","id":1,"name":"John Doe"}
My question is, if the value is not changed in the database, why it gives me the modified object at Line 2 though i have retrieved the object using GORM query (which i guess should retrieve from the database)?
I have also tried using save() after the modification but the result is same.
userObj.save() //doesn't show the changes in database.
My guess is that the object is not being saved to the database because some constraint(s) are invalid. You can determine whether this is the case by replacing your calls to save() with save(failOnError: true). If my guess is correct, an exception will be thrown if saving to the database fails.
When you call the save() method on a domain object, it may not persist in the database immediately. In order to persist the changed value to the database, you would need to do the following.
userObj.save(flush: true)
By using flush, you are telling GORM to persist immediately in the database.
In some cases when validation fails, the data will still not persist in the database. The save() method will fail silently. To catch validation errors as well as save to the database immediately, you would want to do the following
userObj.save(flush:true, failOnError:true)
If validation errors exist, then the GROM will throw ValidationException (http://docs.grails.org/latest/api/grails/validation/ValidationException.html)
You need to consider two things:
If you do save(), it only retains in hibernate session, until you flush it the changes does not persist in database. So, better do save(flush:true, failOnError: true) and put in try/catch block and print exception.
And another important things is, your method in controller needs to be with #Transactional annotation, without it your changes does not persist in database, you will encounter Connection is read-only. Queries leading to data modification are not allowed. exception.
Hope this helps, let me know if your issue is fixed. Happy coding . :)

Grails 2.3.7 Optimistic Locking version is updated every time a Command Object is submitted

I have the following
def save(ACommand command){
...
}
#Validateable
class ACommand implements Serializable
{
ADomainObject bundleDef
}
but every time save is called the version is incremented. So if I open up two browsers and submit a different value in succession, instead of getting an error the second time as I would expect, the value is updated.
I also tried using two different sessions with no difference
Update
If I use breakpoints and submit before the other one is completed it works fine. However, If I let the first complete then submit the second without a refresh the version is updated to the newer one (which I don't want) and the change goes through.
Update 2
When you perform updates Hibernate will automatically check the version property against the version column in the database and if they differ will throw a StaleObjectException. This will roll back the transaction if one is active.
per Grails this should work seems to me.
AFAIK, you need to check the version and handle failures yourself - it doesn't happen automatically. You can do this with code like this:
/**
* Returns a boolean indicating whether the object is stale
*/
protected boolean isStale(persistedObj, versionParam = params.version) {
if (versionParam) {
def version = versionParam.toLong()
if (persistedObj.version > version) {
return true
}
} else {
log.warn "No version param found for ${persistedObj.getClass()}"
}
false
}
you can call isStale from an action like this
def update() {
Competition competition = Competition.get(params.id)
if (isStale(competition)) {
// handle stale object
return
}
// object is not stale, so update it
}
I am not sure what you expect to happen but what you describe sounds correct to me unless you have code in your save() action that is relevant.
You can't expect Hibernate to do anything special here. When your save() action is invoked the instance is retrieved using Hibernate, mutated and then saved. That is all fine as far as Hibernate is concerned.
One way to deal with this is when the form for editing the entity is rendered, render the version of the entity that is being edited to a hidden form field which will be submitted when the entity is saved. In the save action after retrieving the entity from the database compare its version to the version retrieved from the hidden form field and if they don't match you know that the entity was modified between those 2 steps and you can react however is appropriate for your app. Note that since your example is using a command object, data binding is being imposed on the entity before your code ever executes. If that isn't what you want, don't use a command object.
I hope that helps.

Grails Saving Object With Composite Key Throws Error

So, my team has been having multiple issues while upgrading our existing app from Grails 1.3.7 to 2.1.0. The latest headache occurs when trying to save a domain class object that has a composite key based on two other domain objects.
We are hanging Grails on a legacy database which we cannot readily change, so all of the domain classes have custom mappings to hook up with it. Below is a quick, slimmed down version of the domain classes in question.
Class Product {
Short prodKey
String name
static hasMany = [groupProduct: GroupProduct]
//Also includes mapping to legacy db and simple constraints
}
Class Group {
Short groupKey
String name
static hasMany =[ groupProduct: GroupProduct]
//This domain class has several other mappings and variables, but they are not relevant
}
Class GroupProduct {
Group group
Product product
Character indicator
static belongsTo = [Product,Group]
static mapping = {
id composite: ["group", "product"]
group lazy:false, column:"GROUP_KEY", joinTable:"GROUP"
product lazy:false, column:"PROD_KEY", joinTable:"PRODUCT"
version false
}
//Only constraint is indicator is Y or N
}
In the app a user is able to select multiple products for a group to turn on or off via a checkbox list. The parameters contain the groupKey and a list of all checked products. The controller gets an instance of the specified group and then a list of all Products. The products are matched against the list in the parameters, every time a match is found a GroupProduct object is made with the indicator set to 'Y', otherwise a GroupProduct object is made with the indicator set to 'N'.
Class GroupProductController{
//allowedMethods and other actions...
def update = {
def groupInstance = Group.get(params.GroupId)
def groupProducts= []
def products= Products.list()
products.each{
def indicator = ...//code to get value of check box for this Product. Returns either Y or N, works as expected
def groupProduct= new GroupProduct(group:groupInstance ,
product:it,
indicator: indicator)
groupProducts.add(groupProduct)
}
groupInstance.discard()
groupProducts.each{
it.save(failOnError: true, flush:true)//This line throws a DB2 SQL error. SQLCODE=-407
}
}
}
Resulting error is:
org.hibernate.util.JDBCExceptionReporter|DB2 SQL Error: SQLCODE=-407, SQLSTATE=23502, SQLERRMC= , DRIVER=3.50.152
org.codehaus.groovy.grails.orm.hibernate.events.PatchedDefaultFlushEventListener|Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: could not update: [GroupProduct#component[group,product]{product=Product#1, group=Group#938926168}]
at com.controllers.ProductGroupController$_closure2_closure8.doCall(ProductGroupController.groovy:86)
at com.nationwide.nas.beam.controllers.ProductGroupController$_closure2.doCall(ProductGroupController.groovy:79)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)
Caused by: com.ibm.db2.jcc.b.lm: DB2 SQL Error: SQLCODE=-407, SQLSTATE=23502, SQLERRMC= , DRIVER=3.50.152
at com.ibm.db2.jcc.b.wc.a(wc.java:575)
at com.ibm.db2.jcc.b.wc.a(wc.java:57)
at com.ibm.db2.jcc.b.wc.a(wc.java:126)
at com.ibm.db2.jcc.b.tk.b(tk.java:1593)
at com.ibm.db2.jcc.b.tk.c(tk.java:1576)
at com.ibm.db2.jcc.t4.db.k(db.java:353)
at com.ibm.db2.jcc.t4.db.a(db.java:59)
at com.ibm.db2.jcc.t4.t.a(t.java:50)
at com.ibm.db2.jcc.t4.tb.b(tb.java:200)
at com.ibm.db2.jcc.b.uk.Gb(uk.java:2355)
at com.ibm.db2.jcc.b.uk.e(uk.java:3129)
at com.ibm.db2.jcc.b.uk.zb(uk.java:568)
at com.ibm.db2.jcc.b.uk.executeUpdate(uk.java:551)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
... 5 more
The error occurs when trying to save the GroupProduct objects. According to IBM the error code -407 is caused by AN UPDATE, INSERT, OR SET VALUE IS NULL, BUT THE OBJECT COLUMN column-name CANNOT CONTAIN NULL VALUES. However, none of the variables for the GroupProducts are actually null. The Group and Product instances are pulled straight from the database, which means they have already been validated and shouldn't have any constraint violations, and I can see that the indicator field is being set correctly.
There is also no problem when running this code under the original 1.3.7 version of the project. If anyone could shed some light on this I'd be very grateful. Thanks
After much debugging and hunting through code, I managed to find the issue. All of our domain classes extend an abstract base domain, which has a createdTimestamp and updatedTimestamp field. Before doing an insert we set both fields, and before doing an update we update the updatedTimestamp.
The issue was that when we were saving, the new object had a null createdTimestamp field, which was throwing the error. Added in code to check if the GroupProduct object we were making already existed, and if so set the new objects createdTimestamp field to the existing object's before saving. Now everything works as expected.
Its strange that Grails 1.3.7 did not have any issues with this code, though. Only thing I can figure is that it automatically associated the new objects with existing ones in the DB. Probably the strangest behavior change I've found during the upgrade process. Hope this helps anyone running into a similar issue.

Grails : data binding

i'm trying to create some domain objects from xml.
class A {
String name
}
class B {
A a
int something
}
i first created an instance of A,and flushed. when creating B, first map the available attributes.
def b = new B(xml.attributes())
this would map 'something' correctly, but not the object type A. So, I retrieve the instance of A and add like
b.a = A.findByA("id of a")
I could see the object b is constructed (both fields filled in) in the debugger, but it doesn't persist on save(flush:true).
What is wrong in the above assignemt, or should use the id instead (b.a.id = ..)
How can I see what is going wrong in the log file? which trace needs to be enabled. I enabled there in config file
trace 'org.hibernate.SQL', 'org.hibernate.type' (which gives the sql trace for insert, select etc. But not for the above scenario, may be because it doesn't reach to hibernate).
Any pointer, highly appreciated.. thanks.
I would wager to guess that your save() is failing validation. You can add save(failOnError:true) to throw an exception when the validation fails, or add the following code to print each of the errors:
b.errors.allErrors.each {
println it
}
With the debugging tip from Rich, I could narrow down the problem... had to rename the attribute to prevent auto mapping. See a similar issue, and response at http://grails.1312388.n4.nabble.com/domain-controller-and-Failed-to-convert-property-value-of-type-problem-td1357947.html
To create association you must pass an object of A
new B(a:A.get(id))
or
B b = new B()
b.a = A.get(id)
Where id must be Integer or Long
Either I miss some context but class A doesn't have method findByA. There is no such A attribute for class A. Suggest you to use method get for strict findings.

Resources