Grails: Do addTo* and removeFrom* require a call to save? - grails

In the docs some examples have a call to save whereas some others do not. I'm assuming that addTo* needs the call to save whereas removeFrom* doesn't. Am I mistaken?

Neither needs a call to save() in most contexts. What you're seeing in the "some examples" link is a save to the main domain object Author, which gets persisted first, and then the other properties will make it in the database with a proper id to link back to. For example, these two snippets are equivalent as far as persistence is concerned:
def a = new Author(name: "Stephen King")
.addToFiction(fictBook)
.addToNonFiction(nonFictBook)
.save()
and
def a = new Author(name: "Stephen King").save()
a.addToFiction(fictBook)
a.addToNonFiction(nonFictBook)
Sometimes, a save(flush:true) operation is needed if you're after an id for further dependent operations, otherwise it is not necessary.
To test, open two browsers, on one do your app clicking, on the other watch your .../dbconsole - persistence is prompt without the need for explicit saving.

Related

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

Why is this a ReadOnly record?

So I am building an associated object through a main object like so:
item.associated_items.build({name: "Machine", color: "Grey"})
and then in another method calling item.save. However I am getting an ActiveRecord::ReadOnlyRecord error. I read in the docs that
Records loaded through joins with piggy-back attributes will be marked as read only since they cannot be saved.
so I think that is what is happening here. But
I dont't know why that is happening. I have called save on an object with a new associated record before and had no problems.
What do the docs mean when they say "piggy-back attributes"?
Is there a way to make the save happen by doing something like item.associated_items.readonly(false).build(attributes). I tried that and it didnt work, but I'm hoping there is another way.
edit: I just tried
new_associated_item = AssociatedItem.new({attributes})
item.associated_items << new_associated_item
and the later method calls
item.save
and the read only exception still happens.
edit2: MurifoX asked me about how Item is being loaded. The above code is happening in a couple of service objects. The process is
Controller
owner = Owner.includes(:medallions).references(:medallions).find_by_id(params[:id])
later
creator = NoticeCreator.new(owner)
creator.run
NoticeCreator
def initialize #effectively
medallion_notice_creators = []
owner.medallions.some_medallion_scope.each do |medallion|
medallion_notice_creator = MedallionNoticeCreator.new(medallion)
medallion_notice_creator.prepare
medallion_notice_creators << medallion_notice_creator
end
end
later after looping through the medallion notice creators
def prepare
medallion.notices.build(attributes)
end
later
medallion_notice_creators.each do |medallion_notice_creator|
medallion_notice_creator.medallion.save
end
Apologies if the code seems convoluted. There is a bunch of stuff going on and I'm trying to condense the code and anonymize it.
Objects created with joins or includes, which is your case, are marked read-only because you are making a giant query with joins and stuff and preloading nested objects within your main one. (ActiveRecord can become confused with so many attributes and don't know how to build the main object, so it marks readonly on it.)
As you have noticed, this won't happen if you create you object with a simple find, as the only attributes received from the query are from the object itself.
I don't know why you are eager loading all of this associations, maybe it is from some rule in your code, but you should try to create a simple owner object using Owner.find(params[:id]), and lazy loading the associations when needed, so this way you can build nested associations on the simple object and save them.

Why don't unflushed domain objects revert to their "saved" state after discard? Can I get the "clean" version?

Confused here about domain objects in intermediate states.
So here's a class:
class Foo {
Double price
String name
Date creationDate = new Date()
static constraints = {
price min: 50D
creationDate nullable: false
}
}
And this test case:
#TestFor(Foo)
class FooSpec extends Specification {
void "test creation and constraints"() {
when: "I create a Foo within constraints"
Foo f= new Foo(price: 60D, name: "FooBar")
then: "It validates and saves"
f.validate()
f.save()
when: "I make that foo invalid"
f.price=49D
then: "It no longer validates"
!f.validate()
when: "I discard the changes since the last save"
f.discard()
then: "it validates again"
f.validate() //TEST FAILS HERE
}
}
So this test fails on the last validate, because f.discard() doesn't seem to revert f to its "saved" state. Notes on the grails docs appear to indcate this is intended (alothough very confusing to me at least) behavior. That all "discard" does is mark it as not to be persisted (which it wouldn't be anyway if it fails the constraints, so in a case like this, I guess it does nothing right? ) I thought refresh might get me the saved state, or even Foo.get([id]) but none of that works. Foo.get gets the corrupted version and f.refresh throws an nullPointer apparently because creationDate is null and it shouldn't be.
So all that is confusing, and seems like there's no way to get back to saved state of the object if it hasn't been flushed. Is that really true? Which sort of raises the question "In what respect is the object "saved?"
That would seem to mean I would want to be making sure the I'm definitely reading from the db not the cache if I want to be sure I'm getting a valid persisted state of the object, \not some possibly corrupted intermediate state.
A related question -- Not sure the scope of the local cache either -- is it restricted to the session? So if I start another session and retrive that id, I'll get a null because it was never saved, not the corrupt version with the invalid price?
Would appreciate someone explaining the underlying design principal here.
Thanks.
Hibernate doesn't have a process for reverting state, although it seems like it could since it keeps a clean copy of the data for dirty checking. Calling the GORM discard() method calls a few layer methods along the way but the real work is done in Hibernate's SessionImpl.evict(Object) method, and that just removes state from the session caches and disassociates the instance from the session, but it does nothing to the persistent properties in the instance. If you want to undo changes you should evict the instance with discard() and reload it, e.g. f = Foo.get(f.id)

Can't access one model's instance variables from another model) (self.ruby)

I have two models, Draft and Pick. Draft creates an array of available Players in an instance variable named 'available_players'. This is done using the 'before_save' callback. The callback runs the instance method 'start' which in turn runs 'set_active_players'. I've tested all of this in my Draft_spec and I have no problems loading players and having them returned in the available_players array. All my draft specs pass.
The problem is that when I try to access the 'available_players' instance variable from Pick.rb, it returns nil. If I call 'draft.start' (the instance method that should run before Draft.rb saves), I can suddenly access the 'available_players' array... it's like the Draft object is not creating the available_players array even though I have the before_save method in place.
Here is the code that fails inside of Pick.rb:
def available_players_returns_nil
#draft_object.available_players
end
Here is the code that works inside of Pick.rb:
def available_players_working
#draft_object.start
#draft_object.available_players
end
I don't want to have to call start every time I call the method because available_players should not need to reload ALL Players. Please help me access available_players!
Links: failing Pick specs, Pick.rb
EDIT:
I should add that #draft_object is found using
#draft_object = Draft.find(self.draft_id)
For a start, this is wrong:
#draft_object = Draft.find(self.draft_id)
You have an association set up, so use it. You can simply use draft within your Pick object to access the Draft it belongs to. No need to assign it to an instance variable called #draft_object.
Same story with player.
Incidentally, your set_available_players method in Draft is just looping through all of the players and adding them to an instance variable. Why are you doing this? Why don't you simply grab the players directly if you need them in Pick? Like this:
#players = Player.all
Also ... I'm somewhat concerned that pretty much all of your tests are commented out. I hope that's not by design?

why read-only access is writing to my db, in GORM?

In my app, I have a code like this:
// 1
Foo.get(123).example = "my example" // as expected, don't change value in db
// 2
Foo.get(123).bars.each { bar ->
bar.value *= -1 // it's changing "value" field in database!! WHY?
}
note: Foo and Bar are tables in my DB
Why is gorm saving in database is second case?
I don't have any save() method in code.
Tks
SOLVED:
I need to use read() to get a readonly session.
(Foo.discard() also works)
Doc: http://grails.org/doc/latest/guide/5.%20Object%20Relational%20Mapping%20%28GORM%29.html#5.1.1%20Basic%20CRUD
(In the first case, I guess I made mistest)
Both should save, so the first example appears to be a bug. Grails requests run in the context of an OpenSessionInView interceptor. This opens a Hibernate session at the beginning of each request and binds it to the thread, and flushes and closes it at the end of the request. This helps a lot with lazy loading, but can have unexpected consequences like you're seeing.
Although you're not explicitly saving, the logic in the Hibernate flush involves finding all attached instances that have been modified and pushing the updates to the database. This is a performance optimization since if each change had been pushed it would slow things down. So everything that can wait until a flush is queued up.
So the only time you need to explicitly save is for new instances, and when you want to check validation errors.

Resources