1


I have a service where to save a lot of data to db. Using MYSQL I have used like this

Domain1.withTransaction {text->
            def domain1=//Create my domain object to save 
             if(!domain1.save()){
               domain1.errors.each {
                      println it
               }
               throw new RuntimeException('unable to save domain1')
        }
        Domain2.withTransaction {text->
            def domain2=//Create my domain object to save 
             if(!domain2.save()){
               domain2.errors.each {
                      println it
               }
               throw new RuntimeException('unable to save domain2')
        }

My problem if there occurred any problem in saving domain2 i need to roll back domain1 save also.
I need to remove domain1 from db.

Jithin
  • 1,745
  • 4
  • 18
  • 25

4 Answers4

3

Instead of using programatic transaction handling, the Service artifact allows for automatic transaction handling. This typically leads to cleaner and more maintainable code.

You can also use the failOnError:true when you save() to force a RuntimeException to be thrown.

Example below:

class SomeService {

    static transactional = false

    def serviceMethod() {
        def domain1=//Create my domain object to save
        def domain2=//Create my domain object to save
        domain1.save(failOnError:true)
        domain2.save(failOnError:true)
    }
}

UPDATE

I'm revisiting this topic, after I read a response to another topic. Grails Integration Test Does NOT Rollback

Please verify that your dialect is setup as InnoDB as MyISAM tables aren't transactional. This is configured in your Config.groovy

dataSource {
      dialect= org.hibernate.dialect.MySQLInnoDBDialect
      ...
}
Community
  • 1
  • 1
Derek Slife
  • 20,750
  • 1
  • 18
  • 22
  • not working for me. If `domain2.save` cause a RuntimeException i can see the effect of domain1.save in my DB. – Jithin Apr 13 '11 at 07:04
  • Is there any configuration that i need to set for this? – Jithin Apr 13 '11 at 09:19
  • Could you verify that the service is within /grails-app/services? Also, dependency injection is the only way that declarative transactions work. You will not get a transactional service if you use the new operator such as new SomeService() – Derek Slife Apr 13 '11 at 14:08
  • Updated the answer. Please verify that you're dialect is InnoDB – Derek Slife Apr 18 '11 at 04:34
1

Try removing the Domain2.withTransaction {text-> part. You are already inside a transaction with your first call. If you do further work inside the brackets, you should stay within the same transaction and domain1 should be rolled back if you throw and exception after checking domain2.

Oliver Tynes
  • 954
  • 4
  • 11
0

Put that Domain2.withTransaction closure inside Domain1.withTransaction closure, so error in Domain2 transaction will rollback both Domain1 and Domain2 transaction
Like this

Domain1.withTransaction {
      //....
      Domain2.withTransaction {
           //....
      } 
} 
Labeeb Panampullan
  • 34,521
  • 28
  • 94
  • 112
0

If you just want a single transaction which is rolled back when an unhandled unchecked exception is thrown, then don't start a nested transaction. The necessaary code changes are shown below:

Domain1.withTransaction {text->
  def domain1=//Create my domain object to save 
  if (!domain1.save()) {
    domain1.errors.each {
      println it
    }
    throw new RuntimeException('unable to save domain1')
  }

  def domain2=//Create my domain object to save 
    if (!domain2.save()) {
      domain2.errors.each {
        println it
      }
    throw new RuntimeException('unable to save domain2')
  }
}
Dónal
  • 185,044
  • 174
  • 569
  • 824