Domain object retains erroneous properties when in a Service

View: New views
11 Messages — Rating Filter:   Alert me  

Domain object retains erroneous properties when in a Service

by mykol :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

hi all,

i've moved a lot of GORM calls to services instead of controllers. this is due to the fact i need transactions that spans more than one domain class. unfortunately, when erroneous properties are set to the domain class, they get retained or saved. to illustrate:


domain class:

class Book
{
String title
static constraints = {
title(blank: false)
}
}

service:

service method:

def update(book)
{
if(!book.hasErrors() || book.save())
{
//do something
}
else
{
throw new RuntimeException()
}
..
..

}

controller method:

def update = {
def book = Book.get(params['id'])
book.properties = params
if(!book.hasErrors() && bookService.update(book)
        {
            ....
        }
        else
        
...
}



if the "title" i pass is blank, and invoke the update action, when i go to "show" or "browse" actions, i see that the book's title is now blank. although it did enter the "else" clause in the "update" action. but somehow, the book retained the title property inserted to it.


verified that without the service, and putting just "if(!book.hasErrors() || book.save())" to the controller, this behavior is not seen.

am i doing anything wrong?

regards,
mykol

Re: Domain object retains erroneous properties when in a Service

by Burt Beckwith :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I've seen the same thing. I have a utility method in a service that saves a domain instance, and if it's not valid it logs the validation errors and calls discard(). The weird thing that I was seeing was that if there was a validation error for a database constraint (e.g. nullable or unique), it would save the instance and the constraint violation would throw an exception. It never even hit my service method.

I spent a lot of time in a debugger and haven't completely figured out what's going on, but the call to getMetaClass() in the service ends the transaction and triggers a flush. I'm not sure yet why that's happening.

All methods of a transactional service become transactional, even the ones added by Groovy, so a more fine-grained configuration like in traditional Spring using annotations, or even just discovering the "real" methods and making all of them transactional would avoid this.

The core problem is that the instance was updated outside of the service, so dirty checking causes a flush. I reworked everything so I make all changes in a service (the utility method is in the base class that all services extend) and save the instance there. For a few parameters I make an explicit signature, e.g. "void updatePerson(person, username, password)" and extract the parameters in the controller, but if there are several I punt and pass in the whole params map, e.g. "void updatePerson(person, params)".

Burt

> hi all,
> i've moved a lot of GORM calls to services instead of controllers. this is
> due to the fact i need transactions that spans more than one domain class.
> unfortunately, when erroneous properties are set to the domain class, they
> get retained or saved. to illustrate:
>
>
> *domain class:*
>
> class Book
> {
> String title
>  static constraints = {
> title(blank: false)
> }
> }
>
> service:
>
> *service method:*
>
> def update(book)
> {
> if(!book.hasErrors() || book.save())
> {
> //do something
> }
> else
> {
> throw new RuntimeException()
> }
>  ..
> ..
>
> }
>
> *controller method:*
>
> def update = {
> def book = Book.get(params['id'])
> book.properties = params
>  if(!book.hasErrors() && bookService.update(book)
>         {
>             ....
>         }
>         else
>
> ...
> }
>
>
>
> if the "title" i pass is blank, and invoke the update action, when i go to
> "show" or "browse" actions, i see that the book's title is now blank.
> although it did enter the "else" clause in the "update" action. but somehow,
> the book retained the title property inserted to it.
>
>
> verified that without the service, and putting just "if(!book.hasErrors() ||
> book.save())" to the controller, this behavior is not seen.
>
> am i doing anything wrong?
>
> regards,
> mykol
>


signature.asc (204 bytes) Download Attachment

Re: Domain object retains erroneous properties when in a Service

by Jeff Brown-14 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Sat, Apr 11, 2009 at 9:45 AM, Michael Mallete <mrmallete@...> wrote:

> }
> service:
> service method:
> def update(book)
> {
> if(!book.hasErrors() || book.save())
> {
> //do something
> }
> else
> {
> throw new RuntimeException()
> }
> ..
> ..
> }

Do you want && in that condition, not || ?  I assume that your intent
is to throw the exception if save() fails.  That is, in order to _not_
throw the exception the object needs to not have errors _and_ save
needs to succeed.



jb
--
Jeff Brown
SpringSource
http://www.springsource.com/

Autism Strikes 1 in 166
Find The Cause ~ Find The Cure
http://www.autismspeaks.org/

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email



Re: Domain object retains erroneous properties when in a Service

by mykol :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

oops sorry. yeah typo there, my actual codes uses &&

i was able to fix this somewhat. from how i understand, the hibernate session begins at the start of the initial GORM method call. from my code, i saw that i invoked a "get" from the controller to get the Book instance. this is then passed to the service, which in turn invokes it's own GORM methods (hasError and save). i really don't know if a session flush is invoked as soon as the controller invokes the service. 

so what i did was to remove the "get" GORM method (and all GORM in the controller), and instead pass the params map to the service. 

any other ideas?



On Sat, Apr 11, 2009 at 11:35 PM, Jeff Brown <jeff@...> wrote:
On Sat, Apr 11, 2009 at 9:45 AM, Michael Mallete <mrmallete@...> wrote:
> }
> service:
> service method:
> def update(book)
> {
> if(!book.hasErrors() || book.save())
> {
> //do something
> }
> else
> {
> throw new RuntimeException()
> }
> ..
> ..
> }

Do you want && in that condition, not || ?  I assume that your intent
is to throw the exception if save() fails.  That is, in order to _not_
throw the exception the object needs to not have errors _and_ save
needs to succeed.



jb
--
Jeff Brown
SpringSource
http://www.springsource.com/

Autism Strikes 1 in 166
Find The Cause ~ Find The Cure
http://www.autismspeaks.org/

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

   http://xircles.codehaus.org/manage_email




Re: Domain object retains erroneous properties when in a Service

by mykol :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

thanx burt. this is exactly what i did. i passed the whole map to the service instead, and removed all GORM prior to service execution. a bit ugly though at the moment since i have to retrieve the Book instance, pass in the properties, then invoke a validate() so that my gsp's render proper error messages everytime there's a failure. have to go this route since it's all inside a "catch" (my services throw an exception to terminate the transaction)

On Sat, Apr 11, 2009 at 11:24 PM, Burt Beckwith <burt@...> wrote:
I've seen the same thing. I have a utility method in a service that saves a domain instance, and if it's not valid it logs the validation errors and calls discard(). The weird thing that I was seeing was that if there was a validation error for a database constraint (e.g. nullable or unique), it would save the instance and the constraint violation would throw an exception. It never even hit my service method.

I spent a lot of time in a debugger and haven't completely figured out what's going on, but the call to getMetaClass() in the service ends the transaction and triggers a flush. I'm not sure yet why that's happening.

All methods of a transactional service become transactional, even the ones added by Groovy, so a more fine-grained configuration like in traditional Spring using annotations, or even just discovering the "real" methods and making all of them transactional would avoid this.

The core problem is that the instance was updated outside of the service, so dirty checking causes a flush. I reworked everything so I make all changes in a service (the utility method is in the base class that all services extend) and save the instance there. For a few parameters I make an explicit signature, e.g. "void updatePerson(person, username, password)" and extract the parameters in the controller, but if there are several I punt and pass in the whole params map, e.g. "void updatePerson(person, params)".

Burt

> hi all,
> i've moved a lot of GORM calls to services instead of controllers. this is
> due to the fact i need transactions that spans more than one domain class.
> unfortunately, when erroneous properties are set to the domain class, they
> get retained or saved. to illustrate:
>
>
> *domain class:*
>
> class Book
> {
> String title
>  static constraints = {
> title(blank: false)
> }
> }
>
> service:
>
> *service method:*
>
> def update(book)
> {
> if(!book.hasErrors() || book.save())
> {
> //do something
> }
> else
> {
> throw new RuntimeException()
> }
>  ..
> ..
>
> }
>
> *controller method:*
>
> def update = {
> def book = Book.get(params['id'])
> book.properties = params
>  if(!book.hasErrors() && bookService.update(book)
>         {
>             ....
>         }
>         else
>
> ...
> }
>
>
>
> if the "title" i pass is blank, and invoke the update action, when i go to
> "show" or "browse" actions, i see that the book's title is now blank.
> although it did enter the "else" clause in the "update" action. but somehow,
> the book retained the title property inserted to it.
>
>
> verified that without the service, and putting just "if(!book.hasErrors() ||
> book.save())" to the controller, this behavior is not seen.
>
> am i doing anything wrong?
>
> regards,
> mykol
>


Re: Domain object retains erroneous properties when in a Service

by Burt Beckwith :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I've found that you can load the domain instance in the controller (it makes sense to do this there so you can more easily display an appropriate message about missing id param, or existing param but instance not found, etc.), but changes and the call to save() need to be done in the service. The session is open the whole time due to the OpenSessionInView interceptor, but the transaction is what's triggering an early flush.

Burt

> thanx burt. this is exactly what i did. i passed the whole map to the
> service instead, and removed all GORM prior to service execution. a bit ugly
> though at the moment since i have to retrieve the Book instance, pass in the
> properties, then invoke a validate() so that my gsp's render proper error
> messages everytime there's a failure. have to go this route since it's all
> inside a "catch" (my services throw an exception to terminate the
> transaction)
>
> On Sat, Apr 11, 2009 at 11:24 PM, Burt Beckwith <burt@...>wrote:
>
> > I've seen the same thing. I have a utility method in a service that saves a
> > domain instance, and if it's not valid it logs the validation errors and
> > calls discard(). The weird thing that I was seeing was that if there was a
> > validation error for a database constraint (e.g. nullable or unique), it
> > would save the instance and the constraint violation would throw an
> > exception. It never even hit my service method.
> >
> > I spent a lot of time in a debugger and haven't completely figured out
> > what's going on, but the call to getMetaClass() in the service ends the
> > transaction and triggers a flush. I'm not sure yet why that's happening.
> >
> > All methods of a transactional service become transactional, even the ones
> > added by Groovy, so a more fine-grained configuration like in traditional
> > Spring using annotations, or even just discovering the "real" methods and
> > making all of them transactional would avoid this.
> >
> > The core problem is that the instance was updated outside of the service,
> > so dirty checking causes a flush. I reworked everything so I make all
> > changes in a service (the utility method is in the base class that all
> > services extend) and save the instance there. For a few parameters I make an
> > explicit signature, e.g. "void updatePerson(person, username, password)" and
> > extract the parameters in the controller, but if there are several I punt
> > and pass in the whole params map, e.g. "void updatePerson(person, params)".
> >
> > Burt
> >
> > > hi all,
> > > i've moved a lot of GORM calls to services instead of controllers. this
> > is
> > > due to the fact i need transactions that spans more than one domain
> > class.
> > > unfortunately, when erroneous properties are set to the domain class,
> > they
> > > get retained or saved. to illustrate:
> > >
> > >
> > > *domain class:*
> > >
> > > class Book
> > > {
> > > String title
> > >  static constraints = {
> > > title(blank: false)
> > > }
> > > }
> > >
> > > service:
> > >
> > > *service method:*
> > >
> > > def update(book)
> > > {
> > > if(!book.hasErrors() || book.save())
> > > {
> > > //do something
> > > }
> > > else
> > > {
> > > throw new RuntimeException()
> > > }
> > >  ..
> > > ..
> > >
> > > }
> > >
> > > *controller method:*
> > >
> > > def update = {
> > > def book = Book.get(params['id'])
> > > book.properties = params
> > >  if(!book.hasErrors() && bookService.update(book)
> > >         {
> > >             ....
> > >         }
> > >         else
> > >
> > > ...
> > > }
> > >
> > >
> > >
> > > if the "title" i pass is blank, and invoke the update action, when i go
> > to
> > > "show" or "browse" actions, i see that the book's title is now blank.
> > > although it did enter the "else" clause in the "update" action. but
> > somehow,
> > > the book retained the title property inserted to it.
> > >
> > >
> > > verified that without the service, and putting just "if(!book.hasErrors()
> > ||
> > > book.save())" to the controller, this behavior is not seen.
> > >
> > > am i doing anything wrong?
> > >
> > > regards,
> > > mykol
> > >
> >
>


signature.asc (204 bytes) Download Attachment

Re: Domain object retains erroneous properties when in a Service

by mykol :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

ah! so the setting of "properties" should happen inside the service, and not in the controller.

service:

def update(book, params)
{
  book.properties = params
  ...
}

thanx for the tip! will give this a shot

On Sun, Apr 12, 2009 at 12:03 AM, Burt Beckwith <burt@...> wrote:
I've found that you can load the domain instance in the controller (it makes sense to do this there so you can more easily display an appropriate message about missing id param, or existing param but instance not found, etc.), but changes and the call to save() need to be done in the service. The session is open the whole time due to the OpenSessionInView interceptor, but the transaction is what's triggering an early flush.

Burt

> thanx burt. this is exactly what i did. i passed the whole map to the
> service instead, and removed all GORM prior to service execution. a bit ugly
> though at the moment since i have to retrieve the Book instance, pass in the
> properties, then invoke a validate() so that my gsp's render proper error
> messages everytime there's a failure. have to go this route since it's all
> inside a "catch" (my services throw an exception to terminate the
> transaction)
>
> On Sat, Apr 11, 2009 at 11:24 PM, Burt Beckwith <burt@...>wrote:
>
> > I've seen the same thing. I have a utility method in a service that saves a
> > domain instance, and if it's not valid it logs the validation errors and
> > calls discard(). The weird thing that I was seeing was that if there was a
> > validation error for a database constraint (e.g. nullable or unique), it
> > would save the instance and the constraint violation would throw an
> > exception. It never even hit my service method.
> >
> > I spent a lot of time in a debugger and haven't completely figured out
> > what's going on, but the call to getMetaClass() in the service ends the
> > transaction and triggers a flush. I'm not sure yet why that's happening.
> >
> > All methods of a transactional service become transactional, even the ones
> > added by Groovy, so a more fine-grained configuration like in traditional
> > Spring using annotations, or even just discovering the "real" methods and
> > making all of them transactional would avoid this.
> >
> > The core problem is that the instance was updated outside of the service,
> > so dirty checking causes a flush. I reworked everything so I make all
> > changes in a service (the utility method is in the base class that all
> > services extend) and save the instance there. For a few parameters I make an
> > explicit signature, e.g. "void updatePerson(person, username, password)" and
> > extract the parameters in the controller, but if there are several I punt
> > and pass in the whole params map, e.g. "void updatePerson(person, params)".
> >
> > Burt
> >
> > > hi all,
> > > i've moved a lot of GORM calls to services instead of controllers. this
> > is
> > > due to the fact i need transactions that spans more than one domain
> > class.
> > > unfortunately, when erroneous properties are set to the domain class,
> > they
> > > get retained or saved. to illustrate:
> > >
> > >
> > > *domain class:*
> > >
> > > class Book
> > > {
> > > String title
> > >  static constraints = {
> > > title(blank: false)
> > > }
> > > }
> > >
> > > service:
> > >
> > > *service method:*
> > >
> > > def update(book)
> > > {
> > > if(!book.hasErrors() || book.save())
> > > {
> > > //do something
> > > }
> > > else
> > > {
> > > throw new RuntimeException()
> > > }
> > >  ..
> > > ..
> > >
> > > }
> > >
> > > *controller method:*
> > >
> > > def update = {
> > > def book = Book.get(params['id'])
> > > book.properties = params
> > >  if(!book.hasErrors() && bookService.update(book)
> > >         {
> > >             ....
> > >         }
> > >         else
> > >
> > > ...
> > > }
> > >
> > >
> > >
> > > if the "title" i pass is blank, and invoke the update action, when i go
> > to
> > > "show" or "browse" actions, i see that the book's title is now blank.
> > > although it did enter the "else" clause in the "update" action. but
> > somehow,
> > > the book retained the title property inserted to it.
> > >
> > >
> > > verified that without the service, and putting just "if(!book.hasErrors()
> > ||
> > > book.save())" to the controller, this behavior is not seen.
> > >
> > > am i doing anything wrong?
> > >
> > > regards,
> > > mykol
> > >
> >
>


Re: Domain object retains erroneous properties when in a Service

by Burt Beckwith :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I wrote this up in JIRA: http://jira.codehaus.org/browse/GRAILS-4426

Burt

> ah! so the setting of "properties" should happen inside the service, and not
> in the controller.
> *service:*
>
> def update(book, params)
> {
>   book.properties = params
>   ...
> }
>
> thanx for the tip! will give this a shot
>
> On Sun, Apr 12, 2009 at 12:03 AM, Burt Beckwith <burt@...>wrote:
>
> > I've found that you can load the domain instance in the controller (it
> > makes sense to do this there so you can more easily display an appropriate
> > message about missing id param, or existing param but instance not found,
> > etc.), but changes and the call to save() need to be done in the service.
> > The session is open the whole time due to the OpenSessionInView interceptor,
> > but the transaction is what's triggering an early flush.
> >
> > Burt
> >
> > > thanx burt. this is exactly what i did. i passed the whole map to the
> > > service instead, and removed all GORM prior to service execution. a bit
> > ugly
> > > though at the moment since i have to retrieve the Book instance, pass in
> > the
> > > properties, then invoke a validate() so that my gsp's render proper error
> > > messages everytime there's a failure. have to go this route since it's
> > all
> > > inside a "catch" (my services throw an exception to terminate the
> > > transaction)
> > >
> > > On Sat, Apr 11, 2009 at 11:24 PM, Burt Beckwith <burt@...
> > >wrote:
> > >
> > > > I've seen the same thing. I have a utility method in a service that
> > saves a
> > > > domain instance, and if it's not valid it logs the validation errors
> > and
> > > > calls discard(). The weird thing that I was seeing was that if there
> > was a
> > > > validation error for a database constraint (e.g. nullable or unique),
> > it
> > > > would save the instance and the constraint violation would throw an
> > > > exception. It never even hit my service method.
> > > >
> > > > I spent a lot of time in a debugger and haven't completely figured out
> > > > what's going on, but the call to getMetaClass() in the service ends the
> > > > transaction and triggers a flush. I'm not sure yet why that's
> > happening.
> > > >
> > > > All methods of a transactional service become transactional, even the
> > ones
> > > > added by Groovy, so a more fine-grained configuration like in
> > traditional
> > > > Spring using annotations, or even just discovering the "real" methods
> > and
> > > > making all of them transactional would avoid this.
> > > >
> > > > The core problem is that the instance was updated outside of the
> > service,
> > > > so dirty checking causes a flush. I reworked everything so I make all
> > > > changes in a service (the utility method is in the base class that all
> > > > services extend) and save the instance there. For a few parameters I
> > make an
> > > > explicit signature, e.g. "void updatePerson(person, username,
> > password)" and
> > > > extract the parameters in the controller, but if there are several I
> > punt
> > > > and pass in the whole params map, e.g. "void updatePerson(person,
> > params)".
> > > >
> > > > Burt
> > > >
> > > > > hi all,
> > > > > i've moved a lot of GORM calls to services instead of controllers.
> > this
> > > > is
> > > > > due to the fact i need transactions that spans more than one domain
> > > > class.
> > > > > unfortunately, when erroneous properties are set to the domain class,
> > > > they
> > > > > get retained or saved. to illustrate:
> > > > >
> > > > >
> > > > > *domain class:*
> > > > >
> > > > > class Book
> > > > > {
> > > > > String title
> > > > >  static constraints = {
> > > > > title(blank: false)
> > > > > }
> > > > > }
> > > > >
> > > > > service:
> > > > >
> > > > > *service method:*
> > > > >
> > > > > def update(book)
> > > > > {
> > > > > if(!book.hasErrors() || book.save())
> > > > > {
> > > > > //do something
> > > > > }
> > > > > else
> > > > > {
> > > > > throw new RuntimeException()
> > > > > }
> > > > >  ..
> > > > > ..
> > > > >
> > > > > }
> > > > >
> > > > > *controller method:*
> > > > >
> > > > > def update = {
> > > > > def book = Book.get(params['id'])
> > > > > book.properties = params
> > > > >  if(!book.hasErrors() && bookService.update(book)
> > > > >         {
> > > > >             ....
> > > > >         }
> > > > >         else
> > > > >
> > > > > ...
> > > > > }
> > > > >
> > > > >
> > > > >
> > > > > if the "title" i pass is blank, and invoke the update action, when i
> > go
> > > > to
> > > > > "show" or "browse" actions, i see that the book's title is now blank.
> > > > > although it did enter the "else" clause in the "update" action. but
> > > > somehow,
> > > > > the book retained the title property inserted to it.
> > > > >
> > > > >
> > > > > verified that without the service, and putting just
> > "if(!book.hasErrors()
> > > > ||
> > > > > book.save())" to the controller, this behavior is not seen.
> > > > >
> > > > > am i doing anything wrong?
> > > > >
> > > > > regards,
> > > > > mykol
> > > > >
> > > >
> > >
> >
>


signature.asc (204 bytes) Download Attachment

Re: Domain object retains erroneous properties when in a Service

by mykol :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

blogged about it too ;)

http://noisyheads.wordpress.com/2009/04/12/beware-controller-and-service-dirt-and-flush/

On Sun, Apr 12, 2009 at 12:49 AM, Burt Beckwith <burt@...> wrote:
I wrote this up in JIRA: http://jira.codehaus.org/browse/GRAILS-4426

Burt

> ah! so the setting of "properties" should happen inside the service, and not
> in the controller.
> *service:*
>
> def update(book, params)
> {
>   book.properties = params
>   ...
> }
>
> thanx for the tip! will give this a shot
>
> On Sun, Apr 12, 2009 at 12:03 AM, Burt Beckwith <burt@...>wrote:
>
> > I've found that you can load the domain instance in the controller (it
> > makes sense to do this there so you can more easily display an appropriate
> > message about missing id param, or existing param but instance not found,
> > etc.), but changes and the call to save() need to be done in the service.
> > The session is open the whole time due to the OpenSessionInView interceptor,
> > but the transaction is what's triggering an early flush.
> >
> > Burt
> >
> > > thanx burt. this is exactly what i did. i passed the whole map to the
> > > service instead, and removed all GORM prior to service execution. a bit
> > ugly
> > > though at the moment since i have to retrieve the Book instance, pass in
> > the
> > > properties, then invoke a validate() so that my gsp's render proper error
> > > messages everytime there's a failure. have to go this route since it's
> > all
> > > inside a "catch" (my services throw an exception to terminate the
> > > transaction)
> > >
> > > On Sat, Apr 11, 2009 at 11:24 PM, Burt Beckwith <burt@...
> > >wrote:
> > >
> > > > I've seen the same thing. I have a utility method in a service that
> > saves a
> > > > domain instance, and if it's not valid it logs the validation errors
> > and
> > > > calls discard(). The weird thing that I was seeing was that if there
> > was a
> > > > validation error for a database constraint (e.g. nullable or unique),
> > it
> > > > would save the instance and the constraint violation would throw an
> > > > exception. It never even hit my service method.
> > > >
> > > > I spent a lot of time in a debugger and haven't completely figured out
> > > > what's going on, but the call to getMetaClass() in the service ends the
> > > > transaction and triggers a flush. I'm not sure yet why that's
> > happening.
> > > >
> > > > All methods of a transactional service become transactional, even the
> > ones
> > > > added by Groovy, so a more fine-grained configuration like in
> > traditional
> > > > Spring using annotations, or even just discovering the "real" methods
> > and
> > > > making all of them transactional would avoid this.
> > > >
> > > > The core problem is that the instance was updated outside of the
> > service,
> > > > so dirty checking causes a flush. I reworked everything so I make all
> > > > changes in a service (the utility method is in the base class that all
> > > > services extend) and save the instance there. For a few parameters I
> > make an
> > > > explicit signature, e.g. "void updatePerson(person, username,
> > password)" and
> > > > extract the parameters in the controller, but if there are several I
> > punt
> > > > and pass in the whole params map, e.g. "void updatePerson(person,
> > params)".
> > > >
> > > > Burt
> > > >
> > > > > hi all,
> > > > > i've moved a lot of GORM calls to services instead of controllers.
> > this
> > > > is
> > > > > due to the fact i need transactions that spans more than one domain
> > > > class.
> > > > > unfortunately, when erroneous properties are set to the domain class,
> > > > they
> > > > > get retained or saved. to illustrate:
> > > > >
> > > > >
> > > > > *domain class:*
> > > > >
> > > > > class Book
> > > > > {
> > > > > String title
> > > > >  static constraints = {
> > > > > title(blank: false)
> > > > > }
> > > > > }
> > > > >
> > > > > service:
> > > > >
> > > > > *service method:*
> > > > >
> > > > > def update(book)
> > > > > {
> > > > > if(!book.hasErrors() || book.save())
> > > > > {
> > > > > //do something
> > > > > }
> > > > > else
> > > > > {
> > > > > throw new RuntimeException()
> > > > > }
> > > > >  ..
> > > > > ..
> > > > >
> > > > > }
> > > > >
> > > > > *controller method:*
> > > > >
> > > > > def update = {
> > > > > def book = Book.get(params['id'])
> > > > > book.properties = params
> > > > >  if(!book.hasErrors() && bookService.update(book)
> > > > >         {
> > > > >             ....
> > > > >         }
> > > > >         else
> > > > >
> > > > > ...
> > > > > }
> > > > >
> > > > >
> > > > >
> > > > > if the "title" i pass is blank, and invoke the update action, when i
> > go
> > > > to
> > > > > "show" or "browse" actions, i see that the book's title is now blank.
> > > > > although it did enter the "else" clause in the "update" action. but
> > > > somehow,
> > > > > the book retained the title property inserted to it.
> > > > >
> > > > >
> > > > > verified that without the service, and putting just
> > "if(!book.hasErrors()
> > > > ||
> > > > > book.save())" to the controller, this behavior is not seen.
> > > > >
> > > > > am i doing anything wrong?
> > > > >
> > > > > regards,
> > > > > mykol
> > > > >
> > > >
> > >
> >
>


Re: Domain object retains erroneous properties when in a Service

by Daniel Guryca-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi,

That's exactly why I'm doing it withTransaction way - directly within a controller.

I  guess this is a serious bug because it makes services almost unusable for gorm stuff.

I hope it is fixed as soon as possible.

By the way as I can see, 1.1 is very buggy at the moment.
I'm considering Grails for a one really huge web project (after I have successfully tried it for some small intranet applications) ... hope that all JIRA bugs are going to be fixed as soon as possible.

Do you think it is production ready ?
Cheers,
Daniel

On Sat, Apr 11, 2009 at 6:49 PM, Burt Beckwith <burt@...> wrote:
I wrote this up in JIRA: http://jira.codehaus.org/browse/GRAILS-4426

Burt

> ah! so the setting of "properties" should happen inside the service, and not
> in the controller.
> *service:*
>
> def update(book, params)
> {
>   book.properties = params
>   ...
> }
>
> thanx for the tip! will give this a shot
>
> On Sun, Apr 12, 2009 at 12:03 AM, Burt Beckwith <burt@...>wrote:
>
> > I've found that you can load the domain instance in the controller (it
> > makes sense to do this there so you can more easily display an appropriate
> > message about missing id param, or existing param but instance not found,
> > etc.), but changes and the call to save() need to be done in the service.
> > The session is open the whole time due to the OpenSessionInView interceptor,
> > but the transaction is what's triggering an early flush.
> >
> > Burt
> >
> > > thanx burt. this is exactly what i did. i passed the whole map to the
> > > service instead, and removed all GORM prior to service execution. a bit
> > ugly
> > > though at the moment since i have to retrieve the Book instance, pass in
> > the
> > > properties, then invoke a validate() so that my gsp's render proper error
> > > messages everytime there's a failure. have to go this route since it's
> > all
> > > inside a "catch" (my services throw an exception to terminate the
> > > transaction)
> > >
> > > On Sat, Apr 11, 2009 at 11:24 PM, Burt Beckwith <burt@...
> > >wrote:
> > >
> > > > I've seen the same thing. I have a utility method in a service that
> > saves a
> > > > domain instance, and if it's not valid it logs the validation errors
> > and
> > > > calls discard(). The weird thing that I was seeing was that if there
> > was a
> > > > validation error for a database constraint (e.g. nullable or unique),
> > it
> > > > would save the instance and the constraint violation would throw an
> > > > exception. It never even hit my service method.
> > > >
> > > > I spent a lot of time in a debugger and haven't completely figured out
> > > > what's going on, but the call to getMetaClass() in the service ends the
> > > > transaction and triggers a flush. I'm not sure yet why that's
> > happening.
> > > >
> > > > All methods of a transactional service become transactional, even the
> > ones
> > > > added by Groovy, so a more fine-grained configuration like in
> > traditional
> > > > Spring using annotations, or even just discovering the "real" methods
> > and
> > > > making all of them transactional would avoid this.
> > > >
> > > > The core problem is that the instance was updated outside of the
> > service,
> > > > so dirty checking causes a flush. I reworked everything so I make all
> > > > changes in a service (the utility method is in the base class that all
> > > > services extend) and save the instance there. For a few parameters I
> > make an
> > > > explicit signature, e.g. "void updatePerson(person, username,
> > password)" and
> > > > extract the parameters in the controller, but if there are several I
> > punt
> > > > and pass in the whole params map, e.g. "void updatePerson(person,
> > params)".
> > > >
> > > > Burt
> > > >
> > > > > hi all,
> > > > > i've moved a lot of GORM calls to services instead of controllers.
> > this
> > > > is
> > > > > due to the fact i need transactions that spans more than one domain
> > > > class.
> > > > > unfortunately, when erroneous properties are set to the domain class,
> > > > they
> > > > > get retained or saved. to illustrate:
> > > > >
> > > > >
> > > > > *domain class:*
> > > > >
> > > > > class Book
> > > > > {
> > > > > String title
> > > > >  static constraints = {
> > > > > title(blank: false)
> > > > > }
> > > > > }
> > > > >
> > > > > service:
> > > > >
> > > > > *service method:*
> > > > >
> > > > > def update(book)
> > > > > {
> > > > > if(!book.hasErrors() || book.save())
> > > > > {
> > > > > //do something
> > > > > }
> > > > > else
> > > > > {
> > > > > throw new RuntimeException()
> > > > > }
> > > > >  ..
> > > > > ..
> > > > >
> > > > > }
> > > > >
> > > > > *controller method:*
> > > > >
> > > > > def update = {
> > > > > def book = Book.get(params['id'])
> > > > > book.properties = params
> > > > >  if(!book.hasErrors() && bookService.update(book)
> > > > >         {
> > > > >             ....
> > > > >         }
> > > > >         else
> > > > >
> > > > > ...
> > > > > }
> > > > >
> > > > >
> > > > >
> > > > > if the "title" i pass is blank, and invoke the update action, when i
> > go
> > > > to
> > > > > "show" or "browse" actions, i see that the book's title is now blank.
> > > > > although it did enter the "else" clause in the "update" action. but
> > > > somehow,
> > > > > the book retained the title property inserted to it.
> > > > >
> > > > >
> > > > > verified that without the service, and putting just
> > "if(!book.hasErrors()
> > > > ||
> > > > > book.save())" to the controller, this behavior is not seen.
> > > > >
> > > > > am i doing anything wrong?
> > > > >
> > > > > regards,
> > > > > mykol
> > > > >
> > > >
> > >
> >
>


Re: Domain object retains erroneous properties when in a Service

by daniel82 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi everybody

I had the same problem. I solved it by first calling hasErrors() and validate() on the object, before delegating it to the update service. If the object hasErrors or does not validate I am calling discard() on it and rendering the object with it's errors in the view.

This gets even more messy if you have nested objects - like a whole object tree - which you want to update:
http://jira.codehaus.org/browse/GRAILS-4492
Eager fetching solves this problem, but my test sometimes fails and sometimes not... although the webtest never failed. I guess it's a integration test problem.

Best Regards,
Daniel



mykol wrote:
hi all,
i've moved a lot of GORM calls to services instead of controllers. this is
due to the fact i need transactions that spans more than one domain class.
unfortunately, when erroneous properties are set to the domain class, they
get retained or saved. to illustrate:


*domain class:*

class Book
{
String title
 static constraints = {
title(blank: false)
}
}

service:

*service method:*

def update(book)
{
if(!book.hasErrors() || book.save())
{
//do something
}
else
{
throw new RuntimeException()
}
 ..
..

}

*controller method:*

def update = {
def book = Book.get(params['id'])
book.properties = params
 if(!book.hasErrors() && bookService.update(book)
        {
            ....
        }
        else

...
}



if the "title" i pass is blank, and invoke the update action, when i go to
"show" or "browse" actions, i see that the book's title is now blank.
although it did enter the "else" clause in the "update" action. but somehow,
the book retained the title property inserted to it.


verified that without the service, and putting just "if(!book.hasErrors() ||
book.save())" to the controller, this behavior is not seen.

am i doing anything wrong?

regards,
mykol