Benefits of object.get() vs object.read() in Grails - grails

I was skimming some of the Grails documentation and found this bit about the read() method in Grails. If I'm understanding this correctly, you can pull a "read-only" version of an object from the database that will only be saved on an explicit save() call. It seems to me then, that you should use a read() call whenever you have an object that you don't expect to be changed.
But why wouldn't you just always use a read() call? Since the object will be changed to read/write permissions if you save() it anyway, wouldn't it be safer to just read in the object instead of getting it?

You're probably correct - it'd be equivalent in most cases. But Hibernate doesn't require that you call save() since it does dirty checking during a flush and since Grails uses an "Open Session in View" interceptor there will always be a flush at the end of each request. This surprises people who make changes in an instance retrieved by get() that were meant to only be temporary while rendering the view but then the changes get persisted anyway without a save() call. read() would make more sense in that scenario.
One performance optimization is to use http://grails.org/doc/latest/ref/Database%20Mapping/dynamicUpdate.html to only push changed fields to the database. The default is to push all fields whether they're changed or not since then there's no need to generate new SQL for each update. If you read() an instance Hibernate doesn't keep the original data so dynamic update wouldn't be possible since there would be no way to know which fields are dirty.

Related

grails insert into DB without saving, why?

I found something strange in Grails.
I create an object like:
User a = new User()
a.setName("test")
a.save()
after that I call a Method e.g. setActive like:
a.setActive(true)
and DON'T call save() but it is saved in the database. I don't know why?! I think this is dangerous. Any suggestions on this?
Thanks a lot,
Marco
Grails registers an "OpenSessionInView" interceptor that opens a Hibernate session at the beginning of each request, and flushes and closes it after it's finished. This is primarily there for lazy-loaded collections. If there wasn't an open session, after loading the instance it would immediately become disconnected, so trying to access a collection would throw an exception. By keeping the session active, the collections can be resolved.
By default Hibernate automatically pushes changes in persistent instances during a flush, so since the OSIV interceptor flushes at the end of the request, any "dirty" instances like your User instance will have their changes pushed to the database.
To get around this, you can use the read() method to load an existing instance if you only want to modify it temporarily, e.g. for rendering in the GSP, but don't want changes auto-persisted.
This won't work in your case since you're not getting an old instance, you're creating it. In your case just call a.discard() after saving and that will disconnect it from the Hibernate session, and Hibernate won't have anything to push when the flush happens.
After execution of service methods (in case of transactional = true) grails save all changes with domain/persist objects and flush hibernate session.
The same behaviour of Redirect in controller action.
To rollback changes - throw RuntimeException.
Because Hibernate automatically saves all pending updates to persistent objects before it closes the session. This is just how Hibernate works.
If you indicate that you want an object to be persistent by calling .save() on it, Hibernate assumes that you also want any subsequent changes to that object to be saved. I don't see the point in calling a.setActive(true) if you don't want that update to be persisted.

Modify and validate a grails domain object without saving it

How do I use the GORM .get to retrieve an object o, modify some fields, and call o.validate() to find errors without Hibernate saving the object to the DB. discard by itself does not prevent the save. Neither does
clazz.withTransaction { status ->
row.validate(flush:false)
row.discard()
status.setRollbackOnly()
}
This post recommend using a command object but this code will apply to many different domain object. There must be a simple way (some parameter passed to validate?) to give Hibernate the do not save instruction. Do I need to create a new instance every time?
If you use read() instead of get() to retrieve the object it won't be auto-saved during a flush (e.g. at the end of a transaction or a web request). It's not truly read-only, since you can call save() and it will persist - it's just not going to auto-save when dirty.

Modifying object in AfterInsert / AfterUpdate

I have a domain object that holds results of a calculation based on parameters that are properties of the same domain object. I'd like to make sure that any time parameters get changed by the user, it recalculates and gets saved properly into the database.
I am trying to do that with afterInsert (to make sure calculation is correct in the first place), and afterUpdate.
However, since my calculation is trying to modify the object itself, it's not working - throwing various hibernate exceptions.
I tried to put the afterUpdate code into a transaction, but that didn't help. I am afraid I am getting into a circular dependency issues here.
The exception I am getting right now is:
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [esc.scorecard.PropertyScorecard#27]
Are the GORM events designed for simpler use cases? I am tempted to conclude that modifying the object you are in the middle of saving is not the way to go.
Are you using 1.2.0+?
If you are, you can use .withNewSession in the events closures which is supposed to avoid hibernate chaos.
cheers
Lee
Is there any reason against using beforeInsert and beforeUpdate instead of afterInsert and afterUpdate?
If not, switching to the before* event handlers should fix your issue

NHibernate: how to handle entity-based validation using session-per-request pattern, without controllers knowing about ISession

What is the best way to do entity-based validation (each entity class has an IsValid() method that validates its internal members) in ASP.NET MVC, with a "session-per-request" model, where the controller has zero (or limited) knowledge of the ISession? Here's the pattern I'm using:
Get an entity by ID, using an IFooRepository that wraps the current NH session. This returns a connected entity instance.
Load the entity with potentially invalid data, coming from the form post.
Validate the entity by callings its IsValid() method.
If valid, call IFooRepository.Save(entity), which delegates to ISession.Save(). Otherwise, display error message.
The session is currently opened when the request begins and flushed when the request ends. Since my entity is connected to a session, flushing the session attempts to save the changes even if the object is invalid.
What's the best way to keep validation logic in the entity class, limit controller knowledge of NH, and avoid saving invalid changes at the end of a request?
Option 1: Explicitly evict on validation failure, implicitly flush: if the validation fails, I could manually evict the invalid object in the action method. If successful, I do nothing and the session is automatically flushed.
Con: error prone and counter-intuitive ("I didn't call .Save(), why are my invalid changes being saved anyways?")
Option 2: Explicitly flush, do nothing by default: By default I can dispose of the session on request end, only flushing if the controller indicates success. I'd probably create a SaveChanges() method in my base controller that sets a flag indicating success, and then query this flag when closing the session at request end.
Pro: More intuitive to troubleshoot if dev forgets this step [relative to option 1]
Con: I have to call IRepository.Save(entity)' and SaveChanges().
Option 3: Always work with disconnected objects: I could modify my repositories to return disconnected/transient objects, and modify the Repo.Save() method to re-attach them.
Pro: Most intuitive, given that controllers don't know about NH.
Con: Does this defeat many of the benefits I'd get from NH?
Option 1 without a doubt. It's not counter intuitive, it's how NH works. Objects retrieved using NH are persistent and changes will be saved when the session is flushed. Calling Evict makes the object transient which is exactly the behavior you want.
You don't mention it but another option might be to use Manual or Commit FlushMode.
How about a validation service with an IsValid (or something similar) method which validates the object passed to it, if it fails it could publish a ValidationFailed event. Then when your request finishes instead of calling the session's flush you could publish a RequestEnd event. You could then have a handler that listens for both RequestEnd events and ValidationFailed events - if there is a ValidationFailed event then don't flush the session but if not then flush it.
Having said that I just do Option 2!
As Mauricio and Jamie have pointed out in their answers/comments, it's not easy (and probably not desirable) to do exactly what the question asks. NH returns persistent objects, so exposing those objects to the controllers means the controllers are responsible for treating them as such. I want to use lazy loading, so exposing detached instances won't work.
Option 4: Introduce a new pattern that provides the desired semantics
The point of this question is that I'm introducing NH+Repositories to an existing project using a hand-rolled, Active-Record-like DAL. I want code written NH to use patterns similar to the legacy code.
I created a new class called UnitOfWork that is a very thin wrapper over an ITransaction and that knows how to access the ambient session related to the current HttpRequest. This class is designed to be used in a using block, similar to TransactionScope which the team is familiar with:
using (var tx = new UnitOfWork()) {
var entity = FooRepository.GetById(x);
entity.Title = "Potentially Invalid Data";
if (!entity.IsValid()) {
tx.DiscardChanges();
return View("ReloadTheCurrentView");
}
else {
tx.Success();
return RedirectToAction("Success");
}
}
The tx.DiscardChanges() is optional, this class has the same semantics as TransactionScope which means it will implicitly rollback if it is disposed before the success flag is set.
On a greenfield NH project I think it's preferable to use Option 1, as Jamie indicates in his answer. But I think Option 4 is a decent way to introduce NH on a legacy project that already uses similar patterns.

Updating a disconnected LINQ object with MVC Framework RC1

This is a little out there but I have a customer object coming back to my controller. I want to just reconnect this object back to the database, is it even possible? I know there is a datacontext.customers.insertonsubmit(customer), but is there the equivalent datacontext.customers.updateonsubmit(customer)???
This is what I don't like about LINQ-to-SQL.
It generally works fine if you're querying and updating in the same scope, but if you get an object, cache it, and then try to update it later, you can't.
Here's what the documentation says:
Use the Attach methods with entities that have been created in one DataContext, and serialized to a client, and then deserialized back with the intention to perform an update or delete operation. Because the new DataContext has no way of tracking what the original values were for a disconnected entity, the client is responsible for supplying those values. In this version of Attach, the entity is assumed to be in its original value state. After calling this method, you can then update its fields, for example with additional data sent from the client.
Do not try to Attach an entity that has not been detached through serialization. Entities that have not been serialized still maintain associations with deferred loaders that can cause unexpected results if the entity becomes tracked by a second data context.
A little ambiguous IMHO, specifically about exactly what it means by "serialized" and "deserialized".
Also, interestingly enough, here's what it says about the DataContext object:
In general, a DataContext instance is
designed to last for one "unit of
work" however your application defines
that term. A DataContext is
lightweight and is not expensive to
create. A typical LINQ to SQL
application creates DataContext
instances at method scope or as a
member of short-lived classes that
represent a logical set of related
database operations.
So, DataContexts are intended to be tightly scoped - and yet to use Attach(), you have to use the same DataContext that queried the object. I'm assuming/hoping we're all completely misunderstanding what Attach() is really intended to be used for.
What I've had to do in situations like this is re-query the object I needed to update to get a fresh copy, and then do the update.
The customer that you post from the form will not have entity keys so may not attach well, also you may not have every field of the customer available on the form so all of it's fields may not be set.
I would recommend using the TryUpdateModel method, in your action you'll have to get the customer from the database again and update it with the form's post variables.
public ActionResult MySaveAction(int id, FormCollection form)
{
Customer updateCustomer = _Repository.GetCustomer(id);
TryUpdateModel(updateCustomer, "Customer", form);
_Repository.Save(updateCustomer);
}
You will have to add in all your own exception handling and validation of course, but that's the general idea.
You want to use the attach method on the customers table on the data context.
datacontext.customers.Attach(customer);
to reconnect it to the data context. Then you can use SubmitChanges() to update the values in the database.
EDIT: This only works with entities that have been detached from the original data context through serialization. If you don't mind the extra call to the database, you can use the idiomatic method in ASP.NET MVC of retrieving the object again and applying your changes via UpdateModel or TryUpdateModel as #Odd suggests.

Resources