Best practice for transactions and validation errors?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

Best practice for transactions and validation errors?

wwwclaes
Hi, what architectures do you use for rolling back transactions and handling validation errors?

I'm trying to enhance the transaction and validation support in our app and no matter which route I take I get stuck. My primary attempt is:

- Wrap almost everything in a transaction.
- Save domains and redirect if everything validates.
- If validation fails, setRollbackOnly() and instead return from action with modified-but-not-validated domains in the model.
- Show modified-but-not-validated domains in GSP and use hasErrors to highlight errors.

This technique has several problems:

- I get LazyInitializationException because my domains are no longer attached to the Hibernate session. I could do eager fetching, but there are quite a number of hasMany and if these children also have hasMany it's a wobbly road. But still, maybe the best solution?

- If I instead try to attach() the domains after the rollback, I get "HibernateException: reassociated object has dirty collection reference". Seems to be caused by hasMany, as reported here: http://grails.1312388.n4.nabble.com/reassociated-object-has-dirty-collection-reference-or-an-array-td3518169.html

Instead of return during validation errors, I have also tried to store the domains in flash scope, redirect, copy from flash and merge() before returning to GSP. This is the closest I have (so far) come to a solution, but it feels overly complicated and currently also results in the modifications being saved (probably due to the merge and auto-save in Grails).

A third solution, suggested here...

https://github.com/grails/grails-doc/blob/253a59050e12b69b84084e985f28e3a0eca39d62/src/guide/8.1.1%20Transactions%20Rollback%20and%20the%20Session.gdoc

...would be to instead of attach(), do a new read() and then copy errors from the modified-but-not-validated domains. Though I believe this would require a complete rewrite of all my GSP:s because I haven't used the fieldValue-tag to display values.

What techniques do you guys (and girls) use?

Thanks,
Claes
Reply | Threaded
Open this post in threaded view
|

Re: Best practice for transactions and validation errors?

longwa
We typically take the approach of having the controller call services (which are transactional) and then catching any exceptions and setting the exception on the flash for display after a redirect. Occasionally, the objects passed to the service will need to have refresh() or attach() called on them but that is usually not necessary. The other approach that can work is just to eagerly fetch the things that are giving you lazyInitialization in order to ensure everything is already loaded. Typically that will be the most performant solution as well.

Personally, I think the solution of putting the domains in flash scope and doing a copy/merge sounds like a bad idea. I've never seen that done and I prefer to stick to more idiomatic solutions.

-Aaron


On Thu, Apr 24, 2014 at 7:24 AM, Claes Svensson <[hidden email]> wrote:
Hi, what architectures do you use for rolling back transactions and handling validation errors?

I'm trying to enhance the transaction and validation support in our app and no matter which route I take I get stuck. My primary attempt is:

- Wrap almost everything in a transaction.
- Save domains and redirect if everything validates.
- If validation fails, setRollbackOnly() and instead return from action with modified-but-not-validated domains in the model.
- Show modified-but-not-validated domains in GSP and use hasErrors to highlight errors.

This technique has several problems:

- I get LazyInitializationException because my domains are no longer attached to the Hibernate session. I could do eager fetching, but there are quite a number of hasMany and if these children also have hasMany it's a wobbly road. But still, maybe the best solution?

- If I instead try to attach() the domains after the rollback, I get "HibernateException: reassociated object has dirty collection reference". Seems to be caused by hasMany, as reported here: http://grails.1312388.n4.nabble.com/reassociated-object-has-dirty-collection-reference-or-an-array-td3518169.html

Instead of return during validation errors, I have also tried to store the domains in flash scope, redirect, copy from flash and merge() before returning to GSP. This is the closest I have (so far) come to a solution, but it feels overly complicated and currently also results in the modifications being saved (probably due to the merge and auto-save in Grails).

A third solution, suggested here...


...would be to instead of attach(), do a new read() and then copy errors from the modified-but-not-validated domains. Though I believe this would require a complete rewrite of all my GSP:s because I haven't used the fieldValue-tag to display values.

What techniques do you guys (and girls) use?

Thanks,
Claes

Reply | Threaded
Open this post in threaded view
|

RE: Best practice for transactions and validation errors?

wwwclaes
Thank you Aaron for your response.

I have refactored half of the system and settled on eager fetching by adding [fetch: [childB: "eager", ...]] to my findBy:s. I had a minor problem with "children's children", ie A.B.C not being fetched eagerly. I have solved that by adding "lazy: false" to the mapping section in domain B. I'm guessing fetch in findBy cannot be nested, so if I have other children domains that I want to keep lazy as default - I will have to get the children domains separately in my controller and return those explicitly to the view.

One question about the redirect you do after an error. How do you keep and display field that are modified but not yet saved during a redirect?

Let's say you have a user account page and the user has modified the forename and phone number, you want to display modified values and mark the phone number as invalid (but none of them are saved yet). I don't think it is possible to redirect with a model, so I can only think of storing the domain in flash (not so good) or somehow redirect with the params (this pollutes the GSP since for each field you have to check if there's a params object to get the value from before getting it from the actual domain).

I would prefer a redirect though, using return has some downsides.

One additional note: I'm wrapping my controller actions in withTransaction, which then calls a number of service methods. I know it is not recommended, but I couldn't figure out a good architecture that puts all DB-handling in a huge service method and keeps the params handling in the controller. It felt more clean to wrap the entire action and separate into service methods as needed. If this is a really bad idea, then feel free to shout it out loud :-)

/Claes


Date: Sun, 27 Apr 2014 16:29:44 -0400
From: [hidden email]
To: [hidden email]
Subject: Re: [grails-user] Best practice for transactions and validation errors?

We typically take the approach of having the controller call services (which are transactional) and then catching any exceptions and setting the exception on the flash for display after a redirect. Occasionally, the objects passed to the service will need to have refresh() or attach() called on them but that is usually not necessary. The other approach that can work is just to eagerly fetch the things that are giving you lazyInitialization in order to ensure everything is already loaded. Typically that will be the most performant solution as well.

Personally, I think the solution of putting the domains in flash scope and doing a copy/merge sounds like a bad idea. I've never seen that done and I prefer to stick to more idiomatic solutions.

-Aaron


On Thu, Apr 24, 2014 at 7:24 AM, Claes Svensson <[hidden email]> wrote:
Hi, what architectures do you use for rolling back transactions and handling validation errors?

I'm trying to enhance the transaction and validation support in our app and no matter which route I take I get stuck. My primary attempt is:

- Wrap almost everything in a transaction.
- Save domains and redirect if everything validates.
- If validation fails, setRollbackOnly() and instead return from action with modified-but-not-validated domains in the model.
- Show modified-but-not-validated domains in GSP and use hasErrors to highlight errors.

This technique has several problems:

- I get LazyInitializationException because my domains are no longer attached to the Hibernate session. I could do eager fetching, but there are quite a number of hasMany and if these children also have hasMany it's a wobbly road. But still, maybe the best solution?

- If I instead try to attach() the domains after the rollback, I get "HibernateException: reassociated object has dirty collection reference". Seems to be caused by hasMany, as reported here: http://grails.1312388.n4.nabble.com/reassociated-object-has-dirty-collection-reference-or-an-array-td3518169.html

Instead of return during validation errors, I have also tried to store the domains in flash scope, redirect, copy from flash and merge() before returning to GSP. This is the closest I have (so far) come to a solution, but it feels overly complicated and currently also results in the modifications being saved (probably due to the merge and auto-save in Grails).

A third solution, suggested here...


...would be to instead of attach(), do a new read() and then copy errors from the modified-but-not-validated domains. Though I believe this would require a complete rewrite of all my GSP:s because I haven't used the fieldValue-tag to display values.

What techniques do you guys (and girls) use?

Thanks,
Claes

Reply | Threaded
Open this post in threaded view
|

Re: Best practice for transactions and validation errors?

Gil-2
Hi Claes,

Have you tried using Command Objects. This way you can make custom validation and you can save them in your flash scope for your redirect. If an error comes you just return the command object and all what the user wrote in his/her form will be there.

regards

Gil



On Mon, Apr 28, 2014 at 10:27 AM, Claes Svensson <[hidden email]> wrote:
Thank you Aaron for your response.

I have refactored half of the system and settled on eager fetching by adding [fetch: [childB: "eager", ...]] to my findBy:s. I had a minor problem with "children's children", ie A.B.C not being fetched eagerly. I have solved that by adding "lazy: false" to the mapping section in domain B. I'm guessing fetch in findBy cannot be nested, so if I have other children domains that I want to keep lazy as default - I will have to get the children domains separately in my controller and return those explicitly to the view.

One question about the redirect you do after an error. How do you keep and display field that are modified but not yet saved during a redirect?

Let's say you have a user account page and the user has modified the forename and phone number, you want to display modified values and mark the phone number as invalid (but none of them are saved yet). I don't think it is possible to redirect with a model, so I can only think of storing the domain in flash (not so good) or somehow redirect with the params (this pollutes the GSP since for each field you have to check if there's a params object to get the value from before getting it from the actual domain).

I would prefer a redirect though, using return has some downsides.

One additional note: I'm wrapping my controller actions in withTransaction, which then calls a number of service methods. I know it is not recommended, but I couldn't figure out a good architecture that puts all DB-handling in a huge service method and keeps the params handling in the controller. It felt more clean to wrap the entire action and separate into service methods as needed. If this is a really bad idea, then feel free to shout it out loud :-)

/Claes


Date: Sun, 27 Apr 2014 16:29:44 -0400
From: [hidden email]
To: [hidden email]
Subject: Re: [grails-user] Best practice for transactions and validation errors?

We typically take the approach of having the controller call services (which are transactional) and then catching any exceptions and setting the exception on the flash for display after a redirect. Occasionally, the objects passed to the service will need to have refresh() or attach() called on them but that is usually not necessary. The other approach that can work is just to eagerly fetch the things that are giving you lazyInitialization in order to ensure everything is already loaded. Typically that will be the most performant solution as well.

Personally, I think the solution of putting the domains in flash scope and doing a copy/merge sounds like a bad idea. I've never seen that done and I prefer to stick to more idiomatic solutions.

-Aaron


On Thu, Apr 24, 2014 at 7:24 AM, Claes Svensson <[hidden email]> wrote:
Hi, what architectures do you use for rolling back transactions and handling validation errors?

I'm trying to enhance the transaction and validation support in our app and no matter which route I take I get stuck. My primary attempt is:

- Wrap almost everything in a transaction.
- Save domains and redirect if everything validates.
- If validation fails, setRollbackOnly() and instead return from action with modified-but-not-validated domains in the model.
- Show modified-but-not-validated domains in GSP and use hasErrors to highlight errors.

This technique has several problems:

- I get LazyInitializationException because my domains are no longer attached to the Hibernate session. I could do eager fetching, but there are quite a number of hasMany and if these children also have hasMany it's a wobbly road. But still, maybe the best solution?

- If I instead try to attach() the domains after the rollback, I get "HibernateException: reassociated object has dirty collection reference". Seems to be caused by hasMany, as reported here: http://grails.1312388.n4.nabble.com/reassociated-object-has-dirty-collection-reference-or-an-array-td3518169.html

Instead of return during validation errors, I have also tried to store the domains in flash scope, redirect, copy from flash and merge() before returning to GSP. This is the closest I have (so far) come to a solution, but it feels overly complicated and currently also results in the modifications being saved (probably due to the merge and auto-save in Grails).

A third solution, suggested here...


...would be to instead of attach(), do a new read() and then copy errors from the modified-but-not-validated domains. Though I believe this would require a complete rewrite of all my GSP:s because I haven't used the fieldValue-tag to display values.

What techniques do you guys (and girls) use?

Thanks,
Claes


Reply | Threaded
Open this post in threaded view
|

RE: Best practice for transactions and validation errors?

wwwclaes
I'm currently using Command Objects sparsely. It's a good advice for better validation handling and it's definitely on my todo-list to refactor a lot of my if:s in my controllers to separate Command Objects.

Though I don't believe it will make things a lot better when it comes to storing modified-but-not-saved values during error handling. Command Objects are classes just like Domain Objects, and will suffer from the same issues during redirect and/or return.

I have just read a chapter in the soon-to-be-released book "Grails in Action" that contains some nice discussions of these matters in chapter 7. They use return during validation errors, so maybe I should consider that best practice - especially since redirect seems to be hard to handle.

The book also talks briefly about handling validation errors from the GSP-side and states "We'll introduce you to the Fields plugin in a later chapter". That would be an interesting read, but I cannot find that later chapter. It's still in MEAP so it might show up, but I'll try to report that in case it's missed.

That leads to the next question :-) The Fields plugin hasn't been updated in almost two years. Is it still to be considered a best practice to use it? It seems to be a neat idea to DRY your fields and encapsulate validation handling, though adapting all GSP code to something that might not be future-safe is definitely one of the disadvantages.

Thanks,
Claes


Date: Mon, 28 Apr 2014 11:00:39 +0200
From: [hidden email]
To: [hidden email]
Subject: Re: [grails-user] Best practice for transactions and validation errors?

Hi Claes,

Have you tried using Command Objects. This way you can make custom validation and you can save them in your flash scope for your redirect. If an error comes you just return the command object and all what the user wrote in his/her form will be there.

regards

Gil



On Mon, Apr 28, 2014 at 10:27 AM, Claes Svensson <[hidden email]> wrote:
Thank you Aaron for your response.

I have refactored half of the system and settled on eager fetching by adding [fetch: [childB: "eager", ...]] to my findBy:s. I had a minor problem with "children's children", ie A.B.C not being fetched eagerly. I have solved that by adding "lazy: false" to the mapping section in domain B. I'm guessing fetch in findBy cannot be nested, so if I have other children domains that I want to keep lazy as default - I will have to get the children domains separately in my controller and return those explicitly to the view.

One question about the redirect you do after an error. How do you keep and display field that are modified but not yet saved during a redirect?

Let's say you have a user account page and the user has modified the forename and phone number, you want to display modified values and mark the phone number as invalid (but none of them are saved yet). I don't think it is possible to redirect with a model, so I can only think of storing the domain in flash (not so good) or somehow redirect with the params (this pollutes the GSP since for each field you have to check if there's a params object to get the value from before getting it from the actual domain).

I would prefer a redirect though, using return has some downsides.

One additional note: I'm wrapping my controller actions in withTransaction, which then calls a number of service methods. I know it is not recommended, but I couldn't figure out a good architecture that puts all DB-handling in a huge service method and keeps the params handling in the controller. It felt more clean to wrap the entire action and separate into service methods as needed. If this is a really bad idea, then feel free to shout it out loud :-)

/Claes


Date: Sun, 27 Apr 2014 16:29:44 -0400
From: [hidden email]
To: [hidden email]
Subject: Re: [grails-user] Best practice for transactions and validation errors?

We typically take the approach of having the controller call services (which are transactional) and then catching any exceptions and setting the exception on the flash for display after a redirect. Occasionally, the objects passed to the service will need to have refresh() or attach() called on them but that is usually not necessary. The other approach that can work is just to eagerly fetch the things that are giving you lazyInitialization in order to ensure everything is already loaded. Typically that will be the most performant solution as well.

Personally, I think the solution of putting the domains in flash scope and doing a copy/merge sounds like a bad idea. I've never seen that done and I prefer to stick to more idiomatic solutions.

-Aaron


On Thu, Apr 24, 2014 at 7:24 AM, Claes Svensson <[hidden email]> wrote:
Hi, what architectures do you use for rolling back transactions and handling validation errors?

I'm trying to enhance the transaction and validation support in our app and no matter which route I take I get stuck. My primary attempt is:

- Wrap almost everything in a transaction.
- Save domains and redirect if everything validates.
- If validation fails, setRollbackOnly() and instead return from action with modified-but-not-validated domains in the model.
- Show modified-but-not-validated domains in GSP and use hasErrors to highlight errors.

This technique has several problems:

- I get LazyInitializationException because my domains are no longer attached to the Hibernate session. I could do eager fetching, but there are quite a number of hasMany and if these children also have hasMany it's a wobbly road. But still, maybe the best solution?

- If I instead try to attach() the domains after the rollback, I get "HibernateException: reassociated object has dirty collection reference". Seems to be caused by hasMany, as reported here: http://grails.1312388.n4.nabble.com/reassociated-object-has-dirty-collection-reference-or-an-array-td3518169.html

Instead of return during validation errors, I have also tried to store the domains in flash scope, redirect, copy from flash and merge() before returning to GSP. This is the closest I have (so far) come to a solution, but it feels overly complicated and currently also results in the modifications being saved (probably due to the merge and auto-save in Grails).

A third solution, suggested here...


...would be to instead of attach(), do a new read() and then copy errors from the modified-but-not-validated domains. Though I believe this would require a complete rewrite of all my GSP:s because I haven't used the fieldValue-tag to display values.

What techniques do you guys (and girls) use?

Thanks,
Claes


Reply | Threaded
Open this post in threaded view
|

Re: Best practice for transactions and validation errors?

longwa
In reply to this post by wwwclaes

On Mon, Apr 28, 2014 at 4:27 AM, Claes Svensson <[hidden email]> wrote:
One question about the redirect you do after an error. How do you keep and display field that are modified but not yet saved during a redirect?

Typically, when a validation error occurs you will want to just render the view, not do a redirect. Sometimes, if you have a lot of additional state in the view that would be more DRY using a redirect, you can use the chain() method instead.

chain() essentially does what you are suggesting, it does a redirect but stores the model in the flash (for you) and makes it available via the chainModel property. Typically, if it's a command object that we are chaining, I'll do something like this:

def command = chainModel?.command ?: initializeFooCommand()

That allows the method to prefer the chainModel over the default.

Rgds,
Aaron
Reply | Threaded
Open this post in threaded view
|

RE: Best practice for transactions and validation errors?

wwwclaes
Thanks again Aaron,

I wasn't sure how to get hold of the model during chain, that's why I handled flash myself in this little experiment with redirect.

Anyhow, that just got me into deeper problems with transactions - so I'll stick with my current approach which seems to be in line with yours.

/Claes


Date: Tue, 29 Apr 2014 12:30:55 -0400
From: [hidden email]
To: [hidden email]
Subject: Re: [grails-user] Best practice for transactions and validation errors?


On Mon, Apr 28, 2014 at 4:27 AM, Claes Svensson <[hidden email]> wrote:
One question about the redirect you do after an error. How do you keep and display field that are modified but not yet saved during a redirect?

Typically, when a validation error occurs you will want to just render the view, not do a redirect. Sometimes, if you have a lot of additional state in the view that would be more DRY using a redirect, you can use the chain() method instead.

chain() essentially does what you are suggesting, it does a redirect but stores the model in the flash (for you) and makes it available via the chainModel property. Typically, if it's a command object that we are chaining, I'll do something like this:

def command = chainModel?.command ?: initializeFooCommand()

That allows the method to prefer the chainModel over the default.

Rgds,
Aaron
Reply | Threaded
Open this post in threaded view
|

RE: Best practice for transactions and validation errors?

pledbrook
In reply to this post by wwwclaes
wwwclaes wrote
I have just read a chapter in the soon-to-be-released book "Grails in Action" that contains some nice discussions of these matters in chapter 7. They use return during validation errors, so maybe I should consider that best practice - especially since redirect seems to be hard to handle.
This is more the way that scaffolding does it. It is simpler to manage, but results in the old "do you want to resubmit the data" dialog. The Redirect after Post technique is still preferable in my book, but as you've discovered, it's a little harder to manage. Command objects do make this easier though since you don't have to worry about detached domain instances.

wwwclaes wrote
The book also talks briefly about handling validation errors from the GSP-side and states "We'll introduce you to the Fields plugin in a later chapter". That would be an interesting read, but I cannot find that later chapter. It's still in MEAP so it might show up, but I'll try to report that in case it's missed.
That leads to the next question :-) The Fields plugin hasn't been updated in almost two years. Is it still to be considered a best practice to use it? It seems to be a neat idea to DRY your fields and encapsulate validation handling, though adapting all GSP code to something that might not be future-safe is definitely one of the disadvantages.
I think we intended to cover the Fields plugin but it was left out for some reason. It looks like it's not 2.3-ready, which is unfortunate. I may take a look, since it's a valuable plugin for anyone doing a lot of forms.

Peter
Reply | Threaded
Open this post in threaded view
|

RE: Best practice for transactions and validation errors?

pledbrook
Actually, people are using is with Grails 2.3 without problems. The only issue I have run into is if you provide your own _display.gsp or _input.gsp templates for <f:display> and <f:input>:

    https://github.com/gpc/grails-fields/issues/137

My current suggestion is to use the raw() method appropriately in those templates.