Why do Grails command objects commit changes to domain objects by default? - grails

If I use a GORM domain object in my Grails command object, the command object commits changes to the domain object automatically, even though I did not call the save() method.
I want to bind to GORM objects in the command object but without saving or committing changes to the database. If my controller or my service throw an exception, I want transaction rollback.
I can force the behavior I want with the following annotations, but that feels like I'm doing it the hard way.
Controller Class = #Transactional(readOnly = true)
Controller action method = #Transactional
Command Object Class = #Transactional(readOnly = true)
Service Class = #Transactional
Am I doing something wrong, are Grails domain objects supposed to get committed automatically by the command object unless I add all these annotations?

This isn't specific to command objects, it's a general feature of controller actions. By default the open-session-in-view pattern is active, where a Hibernate Session is created and bound to a thread-local before the action runs and it's flushed and closed after the action finishes. Any persistent instances retrieved from the database (either explicitly because of a query, or implicitly during data binding) will stay attached to the open session and are dirty-checked when the session flushes. Any modified instances will have their changes flushed along with other queued actions with or without a save() call.
Making the entire method (or class) transactional and read-only is probably overkill. A more direct approach would be to either retrieve instances as read-only, e.g. using read() instead of get(), calling the readOnly method when doing criteria queries, etc., or 'detaching' modified instances by calling the discard() method on each. Another option is to clear the session at the end of the action so there's nothing to automatically flush, e.g.
AnyDomainClass.withSession { it.clear() }
Note that instances retrieved in 'read-only' mode can have their changes persisted, but Hibernate won't automatically do anything for those instances, it only happens when you explicitly call save().

Related

Grails: Is it possible to query for the updated saved object from afterInsert() or afterUpdate()?

Say I have an model Person. My domain is making use of the GORM triggers as such:
def afterInsert() {
someService.someMethod(this.id)
}
def afterUpdate() {
someService.someMethod(this.id)
}
SomeService would then look like this:
def someMethod(id) {
// This query returns stale object
def person = Person.findById(id)
}
Whenever someMethod() in SomeService gets called, the query does not return the updated object. Instead, it gets the old version/data.
My assumption is that by the time afterUpdate() is called, the changes have not been persisted to the DB yet. I've also tried person.refresh(), but obviously it runs into the same issue.
I've also tried .save(flush:true) in the original service where the object gets saved, but it still returns in the old data in the gorm trigger method.
So is there anyway for me to query for the Person object in such a way that it gets the very latest?
I'm a bit stumped on how this can be implemented.
If possible, avoid database interaction from gorm hooks since you will run into problems when your app grows and you end up debugging a lot of StaleObject or OptimisticLocking -exceptions or other weird behaviour like you are seeing now.
Is better to change the underlying design here if possible and let for example a service to work as a entity persistor here if you need additional logic. Maybe you can use some event-driven mechanism here like activemq.
Injecting a service to a domain class is violation of design and is prevented by default in grails 3.x for performance reasons. Should be always avoided if possible. Active record will cause you problems if you keep pushing it to the edge like this.

When should I call save() on an instance of a domain object in Grails?

From what I've read, save() informs the persistence context that an instance should be saved or updated. However, I have methods in a service that change the property of a domain instance without calling save() and the change appears instantly in my database, no problem.
Is the save() method just a more secure way of knowing that a domain instance will be updated after making a change (and catching errors with the failOnError mapping)? Should it be used EVERY time I change a domain instance's properties or is that overdoing it?
If you create a new instance of a domain class, then a .save() call will tell the underlying Hibernate layer to persist the new object to the database. Without the .save() it won't be persisted to the database.
If you retrieve an object via a .get(myId), then any changes will be automatically persisted to the database at the end of the underlying transaction because Hibernate sees the object as "dirty". The end of a transaction is at the end of a method call to a transactional service or end of a request for controllers. You can call .save() if you want in these instances, but it isn't necessary. It does provide easy access to flushing Hibernate via .save(flush:true) or the failOnError usage for validation.
In addition to schmolly159 answer, I'll just add that you do not need to use save also when the entity you had created is being added to some other entity that already exists in the db (has its own id).
From Grails in Action book:
Notice that we have to call save() on the User object to persist it in the database B. Once the User is attached to the database, though, any additions we make to its object graph (like adding new Post objects via addToPosts() C) are automatically persisted. For this reason, we don’t need to call save() on each Post we create

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.

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.

Resources