Grails - Retrieving fresh instance from database - grails

Can you force the DomainClass.get(id) functionality in Grails to pull a fresh instance from the database rather then using the local copy retrieved previously in the session?
I would like to compare if a property of the instance has changed:
Example:
def myInstance = MyClass.get(params.id)
myInstance.properties = params.data // updates the instance, but does not save it
// stuff happens
def myDbInstance = MyClass.get(params.id) // would like this to pull a fresh copy from the database, unchanged to allow for comparison
if(myDbInstance.value != myInstance.value) {
// do something if has changed
}
Thank you.

Use can use the refresh() method to force an instance to reload from the database.

In case we want to compare the fields updated in view to the ones that are already available in the database, refresh() would update the instance that is yet to be saved in the database.
In that case getPersistentValue('field_name') on the instance would fetch the value in database and can be used for comparisons.
If there is employeeAllocationInstance already available in controller's update action for e.g.,
and we want to compare the updated fields lets say jan allocation
then
existing_allocation = EmployeeAllocation.get(employeeAllocationInstance.id)
would have updated the values in employeeAllocationInstance so we can do
existing_allocation?.getPersistentValue('jan')?.value to get the value that is stored in database (which is yet to be updated)

Related

Overriding data of local copy of database object, Rails

I want to be able to override the instance of a database object without affecting the original
Object #users = User.all with a table attribute :status
#users[0].status = nil
Causes the status attribute to equal to nil when I read it with #users[0].status
However, when I do:
#users.find(1).status = nil
Does not change the status, notably a sql query is called when I use the find command, as such it is the database data that I am receiving
I tried using the .dup and .clone methods
#users=User.all.dup
Did not work
How do I go about with this? Do I have to create a temporary table? I am using postgres

Why does storing a reference to an NSManagedObject prevent it from updating?

This question is poorly phased but this can be better explained in code.
We have a Core Data Stack with private and main contexts as defined by Marcus Zarra here: http://martiancraft.com/blog/2015/03/core-data-stack/
We call a separate class to do a fetch request (main context) and return an array of NSManagedObjects:
NSArray *ourManagedObjects = [[Client sharedClient].coreDataManager fetchArrayForClass:[OurObject class] sortKey:#"name" ascending:YES];
We then do some processing and store a reference:
self.ourObjects = processedManagedObjects
Our view contains a UITableView and this data is used to populate it and that works just fine.
We change the data on our CMS, pull to refresh on the UITableView to trigger a sync (private context) and then call this same function to retrieve the updated data. However, the fetch request returns the exact same data as before even though when I check the sqlite db directly it contains the new data. To get the new values to display I have to reload the app.
I have discovered that if I don't assign the processedManagedObjects to self, the fetch request does indeed return the correct data, so it looks like holding a reference to the NSManagedObject stops it from getting new data from the main context. However I have no idea why that would be.
To clarify, we're pretty sure there's nothing wrong with our Core Data Stack, even when these managed objects are not being updated, other are being updated just fine, it's only this one where we store a local reference.
It sounds like what's going on is:
Managed objects don't automatically update themselves to reflect the latest data in the persistent store when changes are made via a different managed object context.
As a result, if you keep a reference to the objects, they keep whatever data they already had.
On the other hand if you don't keep a reference but instead re-fetch them, you get the new data because there was no managed object hanging around with its old data.
You have a few options:
You could keep the reference and have your context refresh the managed objects, using either the refresh(_, mergeChanges:) method or refreshAllObjects().
If it makes sense for your app, use an NSFetchedResultsController and use its delegate methods to be notified of changes.
Don't keep the reference.
The first is probably best-- refreshAllObjects() is probably what you want. Other options might be better based on other details of your app.
Try setting the shouldRefreshRefetchedObjects property of the fetch request to true. According to the documentation:
By default when you fetch objects, they maintain their current property values, even if the values in the persistent store have changed. Invoking this method with the parameter true means that when the fetch is executed, the property values of fetched objects are updated with the current values in the persistent store.

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

Grails 2.3.7 Optimistic Locking version is updated every time a Command Object is submitted

I have the following
def save(ACommand command){
...
}
#Validateable
class ACommand implements Serializable
{
ADomainObject bundleDef
}
but every time save is called the version is incremented. So if I open up two browsers and submit a different value in succession, instead of getting an error the second time as I would expect, the value is updated.
I also tried using two different sessions with no difference
Update
If I use breakpoints and submit before the other one is completed it works fine. However, If I let the first complete then submit the second without a refresh the version is updated to the newer one (which I don't want) and the change goes through.
Update 2
When you perform updates Hibernate will automatically check the version property against the version column in the database and if they differ will throw a StaleObjectException. This will roll back the transaction if one is active.
per Grails this should work seems to me.
AFAIK, you need to check the version and handle failures yourself - it doesn't happen automatically. You can do this with code like this:
/**
* Returns a boolean indicating whether the object is stale
*/
protected boolean isStale(persistedObj, versionParam = params.version) {
if (versionParam) {
def version = versionParam.toLong()
if (persistedObj.version > version) {
return true
}
} else {
log.warn "No version param found for ${persistedObj.getClass()}"
}
false
}
you can call isStale from an action like this
def update() {
Competition competition = Competition.get(params.id)
if (isStale(competition)) {
// handle stale object
return
}
// object is not stale, so update it
}
I am not sure what you expect to happen but what you describe sounds correct to me unless you have code in your save() action that is relevant.
You can't expect Hibernate to do anything special here. When your save() action is invoked the instance is retrieved using Hibernate, mutated and then saved. That is all fine as far as Hibernate is concerned.
One way to deal with this is when the form for editing the entity is rendered, render the version of the entity that is being edited to a hidden form field which will be submitted when the entity is saved. In the save action after retrieving the entity from the database compare its version to the version retrieved from the hidden form field and if they don't match you know that the entity was modified between those 2 steps and you can react however is appropriate for your app. Note that since your example is using a command object, data binding is being imposed on the entity before your code ever executes. If that isn't what you want, don't use a command object.
I hope that helps.

Update only the changed values on Entity object

how can i automatically update my entity objects changed values and save them to db.
I hava an Action like that
public ActionResult Update()
{
User userToUpdate = new User();
TryUpdateModel<User>(userToUpdate,ValueProvider);
BaseRepository.Context.AttachTo("User",userToUpdate);
BaseRepository.Context.SaveChanges();
return Json("");
}
ValuProvider : has the items that come
from the client as post data.
The problem on this code is the code update all the values but i want to update only the changed values.
How can i find the changed values on my entity object.
You should check out the ObjectContext.ApplyPropertyChanges Method
it is suppose to do what your asking for...
msdn
Two options:
On the View you could know the values that were changed by using Javascript and then you could pass that information to your controller.
You could simply compare the previous values (which you already have since you populated a view) and check each value before updating the DB.
I prefer last option, since at this point you could also check for data validation.
This is really a problem for your data access code, not anything to do with your controller. Pick an ORM that handles this for you and forget about the problem.

Resources