[%author]
[%date]
Tags: #gorm
Groovy has less ceremony than Java. That‘s crystal-clear in exception handling. Java forces us to handle checked exceptions. Groovy does not force us to handle exceptions that we don’t want to handle or that are inappropriate at the current level of code. Any exception we don't handle is automatically passed on to a higher level.
Although the distinction between checked exceptions and runtime exceptions in Groovy is blurred, GORM versions before 6.0.0 did not roll back transactions for checked Exceptions. However, they rollbacked transactions for runtime exceptions.
GrailsĀ® 3.2.0 shipped with GORM 6.0.0. One of the changes introduced by GORM 6.0.0 is that if a transactional method throws an Exception (both checked or runtime exception), the transaction will automatically be rolled back. Thus, same behavior for checked and runtime exceptions as you may expect in Groovy.
The previous behavior is better illustrated with an example:
Given a Checked Exception
package demo import groovy.transform.CompileStatic @CompileStatic class CustomCheckedException extends Exception { }
and a RuntimeException
package demo import groovy.transform.CompileStatic @CompileStatic class CustomRuntimeException extends RuntimeException { }
and the next service:
package demo import grails.transaction.Transactional import groovy.transform.CompileStatic @CompileStatic @Transactional class BookService { void addNewBookChecked() { def book = new Book(name: 'The definitive guide to grails 3') book.save() throw new CustomCheckedException() } void addNewBookRuntime() { def book = new Book(name: 'The definitive guide to grails 3') book.save() throw new CustomRuntimeException() } void addNewBookRegular() { def book = new Book(name: 'The definitive guide to grails 2') book.save() } @Transactional(readOnly = true) int count() { Book.where { }.count() as int } }
The next specification passes for versions of GORM 6.0.0 or beyond.
package demo import spock.lang.Specification import spock.lang.Subject import grails.test.mixin.integration.Integration @Integration class BookServiceIntegrationSpec extends Specification { @Subject BookService bookService def "calling a service method which does not throw any exception results in adding a new book"() { when: bookService.addNewBookRegular() then: bookService.count() == old(bookService.count()) + 1 } def "calling a service method which throws a runtime exception, rollback transaction and it does not add a new book"() { when: bookService.addNewBookRuntime() then: thrown CustomRuntimeException bookService.count() == old(bookService.count()) } // Fails with GORM versions < 6.0.0 def "calling a service method which throws a Checked exception, rollback transaction and it does not add a new book"() { when: bookService.addNewBookChecked() then: thrown CustomCheckedException bookService.count() == old(bookService.count()) } }