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.
Related
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.
A pattern we use here for large dynamic forms is to allow the client to edit pretty much the entire graph including children and removal and addition of children is handled in javascript. Failing use case is when user deletes a property of one of the children and for this example we'll say the email address of the second child is removed and the parm is sent empty.
Example Class Structure
Domain X
Long id
String name
SortedSet<Child> children
Domain Child
Long id
Integer position // used to sort
String name (required)
String email (required)
Parms are named in this manner:
x.id = 1
x.name = 'blah'
x.children[0].id = 1
x.children[0].position = 0
x.children[0].name = 'childa0'
x.children[0].email = 'child0#any.com'
x.children[1].id = 2
x.children[1].position = 1
x.children[1].name = 'child1'
x.children[1].email = ''
Current stored values:
x.id = 1
x.name = 'blah'
x.children[0].id = 1
x.children[0].position = 0
x.children[0].name = 'childa0'
x.children[0].email = 'child0#any.com'
x.children[1].id = 2
x.children[1].position = 1
x.children[1].name = 'child1'
x.children[1].email = 'child1#any.com'
The pattern to update has been:
Browser
user edits all and submits changes
parms are sent as shown above and in this use case user accidentally deleted email address that is required (yes submit could be prevented in this simple case but there are more extensive validation rules and this is just a simple example to illustrate issue)
Server
retrieve current parent domain class using id parm
check current stored version against edited version and ensure it has not changed
apply all parms to current domain object in this manner: x.properties = parms.x
This is where it breaks down. With Grails 2.2.2 the email property of child[1] is updated to an empty string and running validation catches the error. Grails 2.4.4 does NOT update the child value. I know the data binding changed and perhaps there are valid reasons for this change but I can't fathom it. This has worked from 1.3.6 to 2.2.2.
One thing to note is that if I do not retrieve the domain class and instead create a new instance with the parms passed in all of the child parms are set correctly.
X x = new X(params.x)
This seems to indicate some Grails issue as the user guide talks about both methods as equal - one to use for new instances and the other for updates but the examples clearly demonstrate that both methods should handle indexed child properties.
Any help or thoughts are appreciated!
I ran into this problem my self upgrading an application from 2.2.4 to 2.4.4.
Setting grails.databinding.useSpringBinder = true for config.groovy
and adding grails-web-databinding-spring to the pom worked flawlessy for me.
Obviously doing this was a lot less hassle than rewriting all the binding code :-)
Looking into this further Grails 2.4.4 documentation states that it will bind sets positionally if they are indexed (ie. [0]).
http://grails.org/doc/latest/guide/single.html#dataBinding
The section titled "Data Binding and Many-ended Associations" states that these parms will be bound and that new instances of the set will be created if their index is greater than the current set. This was true for me in Grails 2.2.2 but NOT in 2.4.4.
HOWEVER, earlier in the documentation in the section "Binding To Collections And Maps" it states clearly: "When updating existing elements in a Set the Map being assigned to the Set must have id elements in it which represent the element in the Set".
If you are upgrading to Grails 2.4.4 and have any complex form data binding I suggest you look there first to decide whether to upgrade. Due to these changes in data binding this has become the most complex upgrade we've encountered yet with Grails.
The other disappointment with 2.4.4 is that it states you can override the new binding and fall back to the old spring binding but you cannot past 2.4 - at least from what I can gather from others' posts and my attempts. Setting the config property as documented and adding the dependency required resulted in startup runtime errors.
Apparently older versions of grails prior to 2.4.4 would match the child instances to params positionally, i.e. using the indexes ([0],[1], etc.) to match the children not caring if the IDs of the instances matched. The new binder does want the IDs of the instances to match before binding. I had never realised I was not placing the ID in a hidden field as it always worked perfectly.
I had a similar problem after migrating to Grails 2.4.4 (from 2.2.2). After a lot of reading and seaching (you must to definitly read http://docs.grails.org/2.4.4/guide/single.html#dataBinding mencioned in user2863513 post) I've resolved it in a very simple way
In my application I had nested domains relationships. Example:
class A {
String name
static hasMany = [b:B]
}
class B {
String name
static hasMany = [c:C]
}
class C {
String name
}
Due to the form complexity, class C object were not binding correcly. I've resolved this defining the C atribute on class B as a List (remember, has many relationships are implemented as Sets by default). So, my class B changed to:
class B {
String name
static hasMany = [c:C]
List c
}
And voila! Everything works perfectly again.
I'm creating an application using Grails 2.2.4 and Java 7 (these are constraints I cannot change) and I run into an odd behavior when trying to delete multiple entries in a Many-To-Many hasMany Set.
I have a class named Sport that contains the following:
Class Sport{
String name
static hasMany=[category:Category]
static belongsTo = [Category]
}
And another one named Category:
Class Category{
String name
static hasMany=[sports:Sport]
}
Now when in my CategoryController I try to delete multiple Sport instances from sports, my code compiles and runs without errors, but for reason only one of the selected instances is actually deleted.
If I get a Sport list and a Category id from a form and try to run the following code on every objet in the list:
def categoryInstance = Category.get(idCategory)
def sportInstance = Sport.get(idSport)
if(sportInstance!=null){
categoryInstance.removeFromSports(sportInstance)
}
categoryInstance.save()
Only the last instance is deleted.
If I run
def categoryInstance = Category.get(idCategory)
def sportInstance = Sport.get(idSport)
if(sportInstance!=null){
categoryInstance.removeFromSports(sportInstance)
categoryInstance.save()
}
Only the first one is deleted.
Note that this code is run from within a for loop over the params.sport.toList() list.
My guess is that this is either due to the fact that my sports Set is somehow "changed" after the first deletion and therefore Hibernate can't find the next instance, or that my save method commits the first change then "forgets" the next.
Any advice on how I can delete more than one instance at a time?
A workaround is to wrap the code for deleting a single association in a block of withNewSession + withNewTransaction
Sport.withNewSession {
Sport.withNewTransaction {
//delete 1 association here
}
}
It's not very elegant but works. Beware of a potential performance impact, as this will likely have many database roundtrips.
I don't know why this problem occurs, and googling didn't help either.
Another solution = workaround is to explicitly map the relationship as SportCategory, which you can then delete like any other object with SportCategory.delete().
Constantly faced with a problem when I need to compare and manipulate objects that reference other objects. For example:
Class Student {
...
String Name
Integer Age
...
}
Class Stuff {
...
Student student
...
}
When I invoke an instance of Stuff (Stuff.get (id)/load(id)) and will access the Name, Age and other attribute I see in debug mode (stuff .name = null, they're like 'null' although they are not null. It
command when analyzing values of these attributes (stuff
.name == "pen") error occurs.
I need to invoke the instances and compare their values to execute business rules, but do not know how to resolve this issue.
I read something about the inclusion in the configuration Stuff Mapping 'student lazy: false' for all the time you need to load the instance ofstuff , also charge the Student, but that in addition to overload the memory (since stuff is a Domain Great) would solve this case being the only solution to put all references as 'lazy: false' which would slow the application just to make a simple comparison.
Does anyone know how to invoke instances (Stuff), automatically invoking the attribute to be working (student) just to make the comparison of data, without using the 'student lazy: false' that invokes the data at all times?...
Using Grails 2.2.0 e o Groovy 2
Stuff don't have a property called name so you should get MissingPropertyException calling stuff.name. This has nothing to do with the lazy or eager relationship.
You can check the definition of a lazy relationship in the documentation and also the difference between the types of fetch.
To access the name property you need to access the student property before:
Stuff instance = Stuff.get(id)
println instance.student.name //this, if lazy, will trigger a new database query.
If you know that your code will access the Student instance by the relation with Stuff you could fetch both in one database access (eager and not lazy):
Stuff instance = Stuff.withCriteria {
eq('id', id)
fetchMode("student", FetchMode.JOIN)
}
I have 3 domains: A, B, C.
A and C have many-to-many relationship through B.
A and C are searchable.
When I search and get list of A domain all the fields in A are accessible, however relation field is always 'null'. Why I can't access relational field? Why do I get 'null'? If I access object directly, I see a relation, but when searchable returns A domain, I get 'null' on a relation field.
P.S: I tried to make B searchable but it looks like searchable having issue with indexing it on top of that I don't see any point in indexing B since it exists for the sake of many-to-many relationship only.
Please I need help with it. I need to be able to get to C via A on a searchable return.
Thank you.
[Update]: I made a prototype project today (small DB) and made domain B searchable. Now I can access relational field. However in my real project, I don't want to make B searchable since database is big and indexing takes too long. Is there a way around it?
You can refrash all instance in result list or use reload:true property for seach method
searchableService.search(query,[reload:true])
You can find full list of options: http://grails.org/Searchable+Plugin+-+Methods+-+search
reload - If true, reloads the objects from the database, attaching them to a Hibernate session, otherwise the objects are reconstructed from the index. Default is false
Ok, I believe I solved my issue.
First checkout link to a similar question: Grails searchable plugin -- Please give GalmWing some credit.
My solution is following, I am implementing my own controller and following piece of code implements searchable service call:
if (params.q) {
try{
def searchResults = searchableService.search(params.q, params)
searchResults.results.each {
it.refresh()
}
return [carInstanceList:searchResults.results, carInstanceTotal:searchResults.total]
} catch (SearchEngineQueryParseException ex) {
return [parseException: true]
}
As you can see I have a loop where on each item of domain "A" I do refresh. Now refresh gets real record from DB with all the links. Now I return list into GSP and it extracts all of "C" domains associated with the "A" domain.
Now this way you might get performance penalty, but in my case searchable is actually not able to index "B" domain, it is working for a while and then crashes, so I don't have another choice, at least for now.