Grails GORM loading collection of objects issue - grails

I'm trying to load a list of Evaluations in a class named Contributor but I'm missing something as I cannot get my test passing.
My test is creating and saving objects as expected because when I do Evaluations.list() and Contributor.list() and print the values I see the data I saved.
Furthermore, when I select an Evaluation and print the value of its 'contributor' property it appears to be correct.
However, when I select a Contributor and print the values of its 'evaluations' property it is null.
I assume my mapping is the problem?
class Contributor {
String name
static hasMany = [evals:Evaluation]
static mapping = {
id column: 'user_id'
evals joinTable:[name:'evaluation', column:'eval_id', key:'eval_user_id']
version false
}
}
class Evaluation {
Contributor contributor
String comment
static mapping = {
id column:'eval_id'
contributor column:'eval_user_id'
version false
}
}
Thanks.

You shouldn't assign evals like contributor.evals = [eval]. Rather invoke a dynamic method addTo*(): contributor.addToEvals(eval). This will create a Hibernate collection for evals which will be persisted later.
The first one didn't work because Grails doesn't assign other end of association when you assign eval.contributor, i.e. it doesn't call contributor.addToEvals(eval). I have no idea why - there must be some implementation reason.
So contributor remains with the same empty evals in Hibernate cache. If it falls out of cache and you re-read it, it will have evals filled, because the association is mapped only on Eval table's foreign key.
Empty evals are null - it's another Hibernate feature: when a collection is empty, it can be null. Just know it.
Here's an good short post explaining some Hibernate internals: http://blog.xebia.com/2008/03/advanced-hibernate-proxy-pitfalls/

Ahh I've sussed it. The problem is the way I was saving the objects.
The following does not work:
def contributor = new Contributor([name:'Homer']).save(failOnError:true)
def eval = new Evaluation([contributor:contributor, comment:'Must read article']).save(failOnError:true)
This
def contributor = new Contributor([name:'Homer']).save(failOnError:true)
def eval = new Evaluation([contributor:contributor, comment:'a comment']).save(failOnError:true)
contributor.evals = [eval]
contributor.save(failOnError:true)
Or this, does work:
def contributor = new Contributor([name:'Homer'])
def eval = new Evaluation([contributor:contributor, comment:'a comment'])
contributor.evals = [eval]
contributor.save(failOnError:true)
Not sure I fully understand why the top save method doesn't work when selecting data since the id of the contributor is saved on the evaluation.

Related

Grails/Gorm: how to filter a list of domain objects without affecting the database

Say we have something like the standard Book domain object and bookCategory object. In my controller I want to return a subset of list of books to the view. That subset is not achievable using a find query. When I try to filer the return object, it deletes relationships from the database!
I tried this:
class BookCategory{
String name
static hasMany = [books:Book]
}
class Book{
String title
}
def myController() {
def categories
categories = BookCategory.list()
def user = getCurrentUser()
categories.each { category ->
category.books.removeAll { book ->
!isBookBannedForThisUser(book.title, user)
}
[bookCategories: categories]
}
}
The problem is that it permanently removes these books from the categories for all users from the database!!!
I tried putting the method in a service and using a readonly transaction, but this did not help.
I assume that even if I copy all the categories and books into new list, they will still update the DB as they will still have the book IDs (which I need)
Saving to the database when you dont say save() is very dangerous. is there a way to disable this feature completely?
There is a fundamental flaw in your approach. Do not modify your domain instances if you don't intend to have the changes persisted. Doing so is going to cause you headaches.
Your domain model is suppose to be your system of record. Any changes to it are suppose to be persisted.
If you need to gather up data and manipulate it without having it reflected in your domain model then use a DTO (data transfer object) or similar pattern.
Simply calling .discard() will discard the changes you have made from being persisted when the session automatically flushes.
Instead of working against the framework, and disabling behavior, change your approach to be correct.

Referring to Related Record ID In Controller On Save()

I'm still new to Grails and GORM and I got stumped on this and wasn't able to figure out what I am doing wrong. The intent is to automatically relate the record to the logged in user through the Shiro plugin for Grails.
Class User { static hasMany = [stuff: Stuff] }
Class Stuff { static belongsTo = [user:User] }
Class StuffController {
def create = {
params.put('user', User.createCriteria().get{eq('username',SecurityUtils.subject.principal)}.id)
def stuffInstance = new Stuff(params)
stuffInstance.save()
}
}
I saw in the generate-views version of the create scaffold that the relevant field was referred to as name="user.id", but neither it nor variants (such as user_id) seems to work. The query to the Users domain returns the record id necessary, and params.put in this context seems to correctly append the params object with the new value when I render to a test page (so I'm guessing it's not immutable), but this is what I get from the save():
Property [user] of class [class org.stuffing.Stuff] cannot be null
I've even tried flipping it around and going the other way, with the same result:
User.createCriteria().get{eq('username',SecurityUtils.subject.principal)}
.addToStuff(new Stuff(params))`
.save()
Anyone able to enlighten me on what I'm missing here?
Thanks!
EDIT:
Apparently I was being braindead; I was overriding the "create" method, but the default action is "save" in the _form.gsp template, so it wasn't executing that branch.
On the plus side, I did learn about dynamic finders via Burt below, so it wasn't a total wash.
Thanks for your time, guys!
Your code can be a lot cleaner - there's no reason to use createCriteria here. If you're searching by username, use a dynamic finder:
def stuffInstance = new Stuff(params)
def user = User.findByUsername(SecurityUtils.subject.principal)
stuffInstance.user = user
if (!stuffInstance.save()) {
// inspect stuffInstance.errors
}

Modify params before saving domain object

I needed a domain class that held a list of Strings. It seems fairly well-known that GORM can't handle this, so I've worked around it. At first I tried using getters and setters in the domain class, but that caused problems. Then I found on Stack Overflow a way to use afterLoad() and beforeValidate() to rewrite properties as shown below. This has worked well to allow me to turn the List into a String for persistence and back to a List for use in the app.
class Entries {
// persisted to database
String _entry
// exposed to app
List entry
static transients = ['entry'] //don't try to persist the List
def afterLoad() {
// split the String from the database into a List
entry = _entry?.split('\\|')
}
def beforeValidate() {
// join the List into a String for persisting
_entry = entry.join('|')
}
static constraints = {
_entry maxSize:4000
}
}
This works fine programmatically. The only problem is that the Grails scaffolding can't deal with this, even if I try to enter a pipe-delimited string. I understand the reason why is that the scaffolding creates a form field for _entry, so entry is null when it tries to save the object. And beforeValidate() relies on a List of Strings to work.
I tried to get around this in the controller, by setting params.entry = params._entry, prior to the call to new Entries(params). [I recognize that this is not a perfect solution, but this was my first pass at getting the form working.] And then I added a test in beforeValidate() to set entry = _entry if entry was null. Basically:
EntriesController.groovy:
params.entry = params._entry // I added this line
def entriesInstance = new Entries(params)
Entries.groovy:
def beforeValidate() {
if( entry == null ) entry = _entry // I added this line
_entry = entry.join('|')
}
I thought that would allow me to enter pipe-delimited strings into the scaffolded Create Entries form and get something into the database.
To my surprise, though, I found that both entry and _entry were null in beforeValidate(), even though I printed and verified that params contained both keys in the controller. I don't understand why this happens. How did my adding a new key to params result in nulls arriving in the domain class?
The follow-up question is, of course, what's the right way to make the scaffolded Create Entries form accept a pipe-delimited String that makes it into the database?
I needed a domain class that held a list of Strings. It seems fairly well-known that GORM can't handle this, so I've worked around it.
I don't agree with you here
class Xyz {
static hasMany = [entries: String]
}
Should create a seperate table to hold your list of strings (It will actually be a Set). Here are the docs

records being deleted when creating and adding new record via Linq To SQL & MVC3

I am trying to add a new comment to a comment table but all records in the table are being deleted with the exception of the one I added.
As an example: Let say I have an existing comment in the database for customer 1. I want to add a new comment.
In my controller I have the following:
List<CustomerComment> comments = _commentsRepository.CustomerComments.ToList();
CustomerComment newComment = new CustomerComment()
{
CustId = 1,
RevisionNumber = revNumber,
Comment = comment,
Customer = _commentRespository.GetCustById(1),
CommentDate = DateTime.Now,
UserId = 24,
Users = _commentsRepository.GetUserById(24)
};
comments.Add(newComment);
_commentsRepository.SaveComment();
In my repository I have the following:
public Int32 SaveComment(CustomerComment comment)
{
try
{
_DB.SubmitChanges();
}
catch
{
throw;
}
return comment.CommentId;
}
While stepping through I see no changes to the data until after I create the new comment and step into the SaveComment method. What is strange is that it shows the comments already in the table for Delete and the new comment for insert.
Not understanding why it thinks the existing comments should be deleted.
I have also tried InsertOnSubmit but it does the samething so I took it out.
One thing I have noticed is that the existing comment after loading in the controller (comments) has the customer object as null. When I create the new comment I am assigning the customer to the new comment (Customer = _commentRespository.GetCustById(1).) Is this causing the delete and why doesn't the object get created and assigned when loaded.
Some additional information is that I am using POCOs and an XML mapping file.
Maybe you should not add the comment to an in memory storage try adding the new comment to the data context instead. I am presuming in your repository you have the add method... So something like _commentsRepository.add(newComment); shoukd work...
Regardless of that, why are you storing the whole customer in the database and for that matter user? you should be storing only their ids no? when you need read onky data to be thrown out into the view you may require additional data such as the customer and user details, use a dto object for that. Persistance in one thing, viewing data with certain data possibly populated from various tables is another thing...

How to retrieve the latest created db entry?

I have the following class and need to manually increment the nextId field.
class SomeIdClass {
Family family
Integer nextId = 0
long timeCreated = new Date().time }
So far I've been trying to retrieve and the latest db entry to increment it and I'm just not having any luck with that. Or am I just going about it in a totally wrong manner?
Thaks
ps: this is what I tried but get a list of Package.SomeId objects
def si = SomeId.executeQuery(" from SomeId where nextId = (select max( nextId ) from SomeId) ")
My two cents for return the last row in Grails:
DomainClass.find("from DomainClass order by id desc")
You can simply get the last saved value this way:
//works only if the primary key 'id' is non-composite
def lastEntry = SomeIdClass.last(sort: 'id')
//alternative method which will work even for composite primary key
def entryList= SomeIdClass.findAll{[sort:'id',order:'asc']}.last()
You can do this:
def maxNextId = DomainClass.executeQuery("select max(nextId) from DomainClass")[0]
Without seeing the whole context, it's hard to tell what you're doing, but as an aside, this looks pretty questionable. This method to assign ids to domain objects is probably the wrong way to go about it. But in any case, what if a new object gets inserted into the database with a greater nextId in between the time you do the query and use the value?
What about replacing
long timeCreated = new Date().time
with
Date dateCreated
which grails automatically populates, to your domain class?
Then you could do something along the lines of
SomeIdClass.listOrderByDateCreated(max: 1, order: "desc")
Also, you do know that by default grails gives every domain object an id that auto-increments right?
Why not using a sequence? You can use a sequence that is global to all your domain classes or you can define a specific sequence for that domain. You can do something like this:
static mapping = {
id generator: 'sequence', params: [sequence: 'some_name_sequence']
}
..and if for some reason you still need to have a nextId, you can create a get method that returns the value of id, something like:
def getNextId() {
return id
}
If you do this then you would need to define nextId as a transient value.
This of course assuming you don't need id and nextId to be different.
From http://www.hsqldb.org/doc/guide/ch09.html#create_table-section
The last inserted value into an identity column for a connection is available using the function IDENTITY(), for example (where Id is the identity column):
INSERT INTO Test (Id, Name) VALUES (NULL,'Test');
CALL IDENTITY();
So, assuming you're using HSQL, you may be able to do:
SomeIdClass.executeQuery("call identity();")
to get the last inserted ID and add to it. MySQL has its own similar feature if HSQL is not the correct route.
This answer is NOT TESTED.
// retrieve the last person
def p = Person.last()
//get the current id
def currentId = p.id
//increment the id manually
def nextId = currentId+1
You can also use the generator in the domain class mappings.
static mapping = {
table 'PERSON'
id generator: 'increment'
version false
}
Ummh, try
SomeIdClass.where {
// any criteria - or none
}.max('nextId').list()[0]
But of course you should be using a sequence generator for ids.

Resources