Grails save() method bug - grails

For example I have entity person saved in db with the name John and id 1 then:
def person = Person.get(1)
person.name = 'Maria'
person.save()
//after that name still will be John
//but if I save one more time than name be Maria
Addition information is that my domain have inside service, and method that work with this service.
I don't now in what is a problem, maybe somebody had already this situation.

It's not flushed at this moment, changes kept in memory for a while. If you need to force Grails to update DB exactly at this moment, add flush: true parameter:
person.save(flush: true)

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

Is it possible to save a specific id for a grails domain object?

I am in the process of trying to copy the properties of one domain object to another similar domain object (Basically moving retired data from an archive collection to an active one). However, when I try to save with a manually inputed id the save will not actually put anything into the collection.
def item = new Item(style: "631459")
item.id = new ObjectId("537da62d770359c2fb4668e2")
item.save(flush: true, validate: false, failOnError:true)
The failOnError does not throw an exception and it seems like the save works correctly. Also if I println on the item.save it will return the correct id. Am I wrong in thinking that you can put a specific id on a domain object?
You can set the id generator as 'assigned' so then you can put the value that you want and is going to be saved with that value.
class Item {
...
static mapping = {
id generator:'assigned'
}
}
The identifier id is a somewhat sensitive name to use. If you check your dbconsole, you will find that GORM has provided one for you even without asking. When you use that name for yourself, confusion happens. Grails will respect you with the println stuff, but GORM has the last word on how id gets initialized and stored, and it is not listening to you then.
You can rename the id to something else like you see in this post and maybe then you can use the name id for yourself. Otherwise, I suggest leaving id to GORM, and have your own identifier for your old keys. You won't have problems retrieving data anyway and there won't be performance issues.

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)

Prevent duplicate entry for save smartly in grails

I have a domain named thana where I put all the thanaName. But I don't want to save any duplicate name. There may be a lot of way for doing this but which will be much smarter I don't know. Can anyone please help me on this. any example or source code will do the work perfectly. thanks in advance for watching the question.
This sounds like the prefect use case for the unique constraint.
class MyDomain {
String name
OtherDomain related
static constraints = {
name unique: ['related'] // each instance must have a unique name per related
}
}
Edit
Updated based on question in comment. The above will ensure that name is unique for each related. So, for example if MyDomain A has a related instance id of 1 and a name of "Test" no ohter instance of MyDomain with the same related instance can have the name of "Test". However, MyDomain B which has a rleated instance id of 2 can have a name of "Test" since the unique is per "related" in the above example.

Cannot insert new Employee entity using InsertOnSubmit()

I'm facing this exception An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported. when I try to insert a new entity into my Employees table (the master one).
There is a relationship between the master Employees table and the details Orders table, and I'm sure that the relationship between these two tables (and specifically Employee.Orders EntitySet) is the cause of the problem since when I removed the relationship, it returns back to insert into Employees table with no problems.
When I searched for the problem, there was this blog post which I tried to implement but my case is a different than the one in the blog post in these items:
He faces the exception when tries to update (while I try to insert).
The tables architecture is different.
how can I solve this problem?
Here's the insertion code:
Employee emp = new Employee();
emp.Name = empName; // empName is a local variable
// What should I default emp.Orders to?
dc.Employees.InsertOnSubmit(emp);
dc.SubmitChanges();
P.S: My DataContext is defined on class-level in my repository and the exception is being thrown when I call dc.SubmitChanges();. and I didn't Attach any object why does it say that?
Here is an article explaining what you need to do using the Attach and Detach methods:
http://www.codeproject.com/KB/linq/linq-to-sql-detach.aspx
I am guessing it is trying to save something else besides just the employee object or you aren't showing us the full code in your repository. When you instantiate your DataContext object (dc) try setting DeferredLoadingEnabled = false, and ObjectTrackingEnabled = false and see if it works. If it does, try watching the SQL code in SQL Server Profiler and see if it is modifying other objects that may have came from a different context like the message says.
var dc = new MyDataContext()
{
DeferredLoadingEnabled = false,
ObjectTrackingEnabled = false
};
My bet is on the primary key.
Are you sure the primary key is also set on auto increment?
Did you
try changing the name, does it work then?
What happens if you remove
all rows from your DB? can you insert one then?

Resources