Grails saving domain object with invalid state

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

Grails saving domain object with invalid state

Nicolás Dijkstra
Hello,

i have a scenario in a Grails 1.1 application where a controller populates a domain object and passes it to a service for validation and saving (some business logic is also included in the service method being called). The service has transactional=true and i found that the object is saved when passed as a parameter in the call from the controller to the service method even calling domainObj.discard() inside the service method, doesn't discard (because the object is saved already but not necesarilly valid tough). Is this supposed to be that way? Where is the transaction created? In the controller or in the service?

I also found that changing transactional=false makes the service work as expected.

Here's a step-by-step if the former explanation was not clear enough (most probably due to my poor english writing):
1.- Controller finds domain object by params.id
2.- Controller populates domain object with data from params map
3.- Controller calls service.saveObj(domainObj)
3.1.- Domain object is saved (even if it's not valid)
3.2.- Service method is called
3.3.- A call to domainObj.discard() doesn't work!!

Any help or clue will be appreciated.

Regards,
Nicolás
Reply | Threaded
Open this post in threaded view
|

Re: Grails saving domain object with invalid state

pledbrook
> Here's a step-by-step if the former explanation was not clear enough (most
> probably due to my poor english writing):
> 1.- Controller finds domain object by params.id
> 2.- Controller populates domain object with data from params map
> 3.- Controller calls service.saveObj(domainObj)
> 3.1.- Domain object is saved (even if it's not valid)
> 3.2.- Service method is called

A transactional service method will automatically flush the Hibernate
session when it completes. Do you call discard() from within the
service method, or afterwards in the controller? If the latter, it's
too late because the transaction flushed the session.

Try moving the call to discard() into the service method.

Cheers,

Peter

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

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|

Re: Grails saving domain object with invalid state

Nicolás Dijkstra
Peter,

discard() is being called from within the service method. I debugged the domain object and noticed it is saved to de database with the call to the service method.

Nicolás

On Tue, Oct 27, 2009 at 3:52 AM, Peter Ledbrook <[hidden email]> wrote:
> Here's a step-by-step if the former explanation was not clear enough (most
> probably due to my poor english writing):
> 1.- Controller finds domain object by params.id
> 2.- Controller populates domain object with data from params map
> 3.- Controller calls service.saveObj(domainObj)
> 3.1.- Domain object is saved (even if it's not valid)
> 3.2.- Service method is called

A transactional service method will automatically flush the Hibernate
session when it completes. Do you call discard() from within the
service method, or afterwards in the controller? If the latter, it's
too late because the transaction flushed the session.

Try moving the call to discard() into the service method.

Cheers,

Peter

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

   http://xircles.codehaus.org/manage_email



Reply | Threaded
Open this post in threaded view
|

Re: Grails saving domain object with invalid state

pledbrook
> discard() is being called from within the service method. I debugged the
> domain object and noticed it is saved to de database with the call to the
> service method.

Could you post the code that's in your service method?

Thanks,

Peter

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

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|

Re: Grails saving domain object with invalid state

Nicolás Dijkstra
Here goes the service method code.

class AssignmentTaskService {
    def projectService

    boolean transactional = false

    boolean saveTask(AssignmentTask task) {
        if (task.absence && !task.absence.isShiftEnabled) {
            task.shift1Start = null
            task.shift1End = null
            task.shift2Start = null
            task.shift2End = null
            task.tripTime = 0F
        }

        if (task.validate()) {
            if (task.absence?.endsAssignment) {
                projectService.endAssignment(task.assignment, task.date, task.absence)
            }
            task.save()
            return true
        } else {
            task.discard()
            return false
        }
    }
}


On Tue, Oct 27, 2009 at 10:05 AM, Peter Ledbrook <[hidden email]> wrote:
> discard() is being called from within the service method. I debugged the
> domain object and noticed it is saved to de database with the call to the
> service method.

Could you post the code that's in your service method?

Thanks,

Peter

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

   http://xircles.codehaus.org/manage_email



Reply | Threaded
Open this post in threaded view
|

Re: Grails saving domain object with invalid state

Nicolás Dijkstra
Hi Peter, any clues about this strange behaviour?

The only workaround i found was changing the service's transactional flag to false

Regards,
Nicolás

2009/10/27 Nicolás Dijkstra <[hidden email]>
Here goes the service method code.

class AssignmentTaskService {
    def projectService

    boolean transactional = false

    boolean saveTask(AssignmentTask task) {
        if (task.absence && !task.absence.isShiftEnabled) {
            task.shift1Start = null
            task.shift1End = null
            task.shift2Start = null
            task.shift2End = null
            task.tripTime = 0F
        }

        if (task.validate()) {
            if (task.absence?.endsAssignment) {
                projectService.endAssignment(task.assignment, task.date, task.absence)
            }
            task.save()
            return true
        } else {
            task.discard()
            return false

        }
    }
}


On Tue, Oct 27, 2009 at 10:05 AM, Peter Ledbrook <[hidden email]> wrote:
> discard() is being called from within the service method. I debugged the
> domain object and noticed it is saved to de database with the call to the
> service method.

Could you post the code that's in your service method?

Thanks,

Peter

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

   http://xircles.codehaus.org/manage_email




Reply | Threaded
Open this post in threaded view
|

Re: Grails saving domain object with invalid state

pledbrook
> Hi Peter, any clues about this strange behaviour?
> The only workaround i found was changing the service's transactional flag to
> false

Not sure. I'm hoping that Burt comes along with his Hibernate/Spring
fu to explain why the session may appear to be flushed at the start of
the transaction. You're definitely not calling save() from the
controller? You're just binding the "params" map to an existing
AssignmentTask?

Peter

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

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|

Re: Grails saving domain object with invalid state

Graeme Rocher-3
On Fri, Nov 6, 2009 at 7:01 AM, Peter Ledbrook <[hidden email]> wrote:
>> Hi Peter, any clues about this strange behaviour?
>> The only workaround i found was changing the service's transactional flag to
>> false
>
> Not sure. I'm hoping that Burt comes along with his Hibernate/Spring
> fu to explain why the session may appear to be flushed at the start of
> the transaction. You're definitely not calling save() from the
> controller? You're just binding the "params" map to an existing
> AssignmentTask?

I believe this is the Grails 1.1 bug where we were proxying the
getMetaClass() method. So

getMetaClass().invokeMethod("transactionalMethod", null)

Resulted in one transaction for getMetaClass (and associated flush)
and a second for the invocation of "transactionalMethod"

Cheers

>
> Peter
>
> ---------------------------------------------------------------------
> To unsubscribe from this list, please visit:
>
>    http://xircles.codehaus.org/manage_email
>
>
>



--
Graeme Rocher
Head of Grails Development
SpringSource - A Division of VMware
http://www.springsource.com

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

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|

Re: Grails saving domain object with invalid state

burtbeckwith
Right, so the workaround I used was to pass the updated request parameters (or the whole params if there are a lot) to the service method and have it make all of the changes and then save. That way the flush that was triggered by the getMetaClass() call didnt' have any effect:

class AssignmentTaskController {

   def assignmentTaskService

   def update = {
      def task = AssignmentTask.get(params.id)
      assignmentTaskService.saveTask task, params
      ...
   }
}

class AssignmentTaskService {
   def projectService

   boolean transactional = true

   boolean saveTask(AssignmentTask task, params) {
      task.params = params
      if (task.absence && !task.absence.isShiftEnabled) {
      ...
      }
   }
}

Burt

> On Fri, Nov 6, 2009 at 7:01 AM, Peter Ledbrook <[hidden email]> wrote:
> >> Hi Peter, any clues about this strange behaviour?
> >> The only workaround i found was changing the service's transactional flag to
> >> false
> >
> > Not sure. I'm hoping that Burt comes along with his Hibernate/Spring
> > fu to explain why the session may appear to be flushed at the start of
> > the transaction. You're definitely not calling save() from the
> > controller? You're just binding the "params" map to an existing
> > AssignmentTask?
>
> I believe this is the Grails 1.1 bug where we were proxying the
> getMetaClass() method. So
>
> getMetaClass().invokeMethod("transactionalMethod", null)
>
> Resulted in one transaction for getMetaClass (and associated flush)
> and a second for the invocation of "transactionalMethod"
>
> Cheers
> >
> > Peter
> >
> > ---------------------------------------------------------------------
> > To unsubscribe from this list, please visit:
> >
> >    http://xircles.codehaus.org/manage_email
> >
> >
> >
>
>
>
>

signature.asc (204 bytes) Download Attachment