Removing multiple items from a Grails One to Many relationship - grails

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().

Related

Grails connect database fields

I'm trying to learn Grails but am still pretty much on beginner level.
I made a tiny application, where you can basically add events, and people can write reviews about them and rate them.
So I have an eventController and reviewController. The rating is just an Integer in a review. But now I want to show an overall rating for an event. So the events rating should be the average value of the corresponding ratings value.
This is my event domain code where the rating is initially set, I left out the constraints and toString:
class Event {
Double dRating = 2
static hasMany = [ratings:Rating]
}
The controller is simply:
class EventController {
def scaffold = Event
}
The rating domain file:
class Rating {
String comment
java.util.Date date = new java.util.Date()
Integer rating
Event event
}
and the Rating Controller is:
class RatingController {
def scaffold = Rating
}
Hope I didn't make mistakes, I had to translate variable names so they're understandable.
I'm guessing that where dRating is set, I could somehow add some calculation, but I don't even know how to access the values of the rating, and everything I try ends in lots of errors and having to restart the app again and again. I tried adding calculations in the controller, but there I don't know how to get the value into the database. So where do I actually put the logic, and how do I access the values?
If any other file is important, please tell me.
Hoping to get some hints on how to start doing this, I didn't think it would be this hard.
At first I would recommend start reading grails doc. Later from your question it is not much clear what you are asking a there could be several places or possibilities of setting up the value in domain field and persist it to database. I'm telling all of these which are known to me:
If it is generalised calculation that needs to be applied to your dRating field, then create a setter with standard bean naming convention and do this there. For example, you want to find percentage from say 1000 and then add it to dRating
class Event {
static hasMany = [ratings:Rating]
Double dRating
void setDRating(Double value){
this.dRating = value * 100/1000 // alternatively value /10
}
}
Do it in commandObject: If you don't want to put certain calculations and validation in domain then put these in command objects. See this. You can at any point of time assign values from command object to domain object by .properties binding mechanism. For example,
yourDomainObject.properties = yourcommandObjectObject.properties
Remember properties having same name would be binded.
Do it in service: You can do your calculations in service method, inject that service into your controller and call that method to perform calculations and even to persist to db as well. Remember services are by default transactional.
Hope it helps!

Lazy Eager loading Grails GORM

Ok i'm wondering what is the best way to implement my scenario.
My objects are as follows:
ProjectCategory has many Projects. Projects have a ProjectStatus. ProjectStatus has a property called name and name can be either "Closed" or "Open" etc...
I'm trying to display on a page all categories and the number of opened projects for that category next to the category name.
How would I go about doing that. The problem I'm seeing is that (using grails gorm) is that by default you cannot do something like
category.findAll{ it.status.name == "Opened" }.size()
because the objects are not loaded that deep. Now If I forced them to load, now for all categories I'm potentially loading a bunch of projects just to get the status. Wouldn't the system take a huge hit in performance the higher amount of projects you have?
The thought of creating a counter in the category and updating it every time a project status changes makes me cringe.
I must just be sleep deprived because I can't see what the proper way of doing this would be. If the way I mentioned first with the .findAll is the way to go, do I really have to worry about performance? How would I go about implementing that?
Thanks in advance for all your help.
I would use HQL. Assuming Projects belong to a ProjectCategory, you could add something like this to your ProjectCategory class:
class ProjectCategory {
// Fields/Methods
def getOpenedProjectsCount() {
ProjectCategory.executeQuery("SELECT count(*) FROM Projects p WHERE p.projectCategory = :projectCategory AND p.projectStatus.name = 'Opened'", [projectCategory: this])
}
}
Then when you have a ProjectCategory instance you can use the openedProjectsCount property:
def projectCategory = ProjectCategory.get(123)
projectCategory.openedProjectsCount

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: Updating data in relationship

I've started a little project using Grails 2.2.3 and I'm now wondering what's the right/most efficient way to update a model with relationship.
class Computer extends Inventory {
String hostName
...
static hasMany = [memories: Memory]
}
class Memory extends Inventory {
Integer capacity
String bank
...
static belongsTo = [computer: Computer]
}
Data input is scheduled regularly from external source, no user interacts with a view.
When just doing a mem.each { computer.addToMemories(it) } there will be a duplicate Memory entry for each scheduled run. I can solve this be checking if a corresponding entry exists:
mem.each {
if (!Memory.findWhere(it)) computer.addToMemories(it)
}
This would work if new mem modules are added to a computer. But imagine, modules are removed from a computer. You could create a nested loop through saved and given modules and compare them. But that's some coding and not very nice. Or you could first do a removeAll() and let then add all modules as new ones. But that's not very nice, too. And id would grow very fast.
Do you know a smarter way to handle this situation? Isn't there a possibility to say "Hey model, here is the latest set of mem modules for this computer. Add it if it's an new one, update it if has changed, or delete it if it has been removed from the computer." Or: "Update your information to this set."
Any ideas?
Thanks in advance,
Chris

What is the best way to declare sorted association in grails domain classes?

It seems that there are two different ways of declaring sorted associations in Grails :
Method 1 (see here) using default sort order
class Book {
String title
}
class Author {
static hasMany = [books : Book]
static mapping = { books sort: "title"}
}
Method 2 (see here) using SortedSet
class Book implements Comparable {
String title
int compareTo(obj) {
title <=> obj.title
}
}
class Author {
SortedSet books
static hasMany = [books : Book]
}
I am not sure which one to use and what is the difference (if any), pros and cons between using one against the other.
I would appreciate any clarification.
Thank you
I started to dig into how this all works and then found that method 1 is actually busted in current versions of grails (tested in both 1.2.1 and 1.3). When you actually try to retrieve an author and look at it's books, it throws an exception
There is an open defect for it (4089) which has been open for quite a while.
Here's the exception that gets thrown:
ERROR util.JDBCExceptionReporter - Column not found: BOOKS0_.TITLE in statement [select books0_.author_books_id as author1_0_, books0_.book_id as book2_0_ from author_book books0_ where books0_.author_books_id=? order by books0_.title]
If and when they finally fix it, the differences between the two methods are that in method one, sorting is done at the database level. As you can see in the exception above, GORM was trying to do an "order by books0_.title" which would use any database index on the book.title field and return the objects in that order.
The second method would sort the objects in memory at the time that they get inserted into the set (using the compareTo method that was defined).
Until the current bug is fixed, I'd use method 2 because it's the only thing that works. It should be fine for relatively small collections of things. After it's fixed, I'd potentially prefer method 1 as the database should be quicker at doing the sorting with an index on the sort field.

Resources