Tachy
Tachy

Reputation: 1021

Using Grails Exector Plugin's runAsync , Why do I need a transaction to save a Domain object?

Here's roughly what I'm doing in a service:

runAsync 
{
  <some work here>
  myDomainObject.merge()     
}

I get an error saying "No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here". I know for sure that the code is being run asynchronously, so it would seem that the Executor Plugin is setup correctly.

So I tried this next, thinking the domain object "myDomainObject" must not be bound in this thread although the thread has a hibernate session thanks to the executor plugin:

runAsync
{
  <work>
  def instance2= MyDomainObject.get(myDomainObject.id) // works
  instance2.field1=123
  instance2.save() // fails
}

I get the same error here and interestingly, the get() succeeds in bringing the correct data and setting it to instance2. It's only the "save()" that fails. I know this because I've stepped through the code in a debugger.

Finally, if I do the following, everything works:

runAsync 
    {
      <some work here>
      MyDomainObject.withTransaction {
           myDomainObject.field1=123
           myDomainObject.merge()
      }
    }

I don't understand why this transaction is required since I haven't set the service I'm writing the above code in to be transactional. I know there must be something fundamental that I don't know here, but I can't find out what it is.

Upvotes: 0

Views: 2041

Answers (1)

dmahapatro
dmahapatro

Reputation: 50265

Looks like you answered your own question :)

I don't understand why this transaction is required since I haven't set the service I'm writing the above code in to be transactional.

Have a look at the NOTE ON TRANSACTIONS. You need your service to be transactional.

NOTE ON TRANSACTIONS: keep in mind that this is spinning off a new thread and that any call will be outside of the transaction you are in. Use .withTransaction inside your closure, runnable or callable to make your process run in a transaction that is not calling a transactional service method (such as when using this in a controller).

UPDATE
Try the service class like below:

class MyService{

    def someMethod(){
        runAsync {
            anotherMethod()
        }
    }

    def anotherMethod(){ 
       <work>
       def instance2= MyDomainObject.get(myDomainObject.id) // works
       instance2.field1=123
       instance2.save() // should work as well
    }
}

Upvotes: 2

Related Questions