A continuacion vamos a hacer una serie de reglas para seguir haciendo buen codigo Grails:
- Grails solo hace rollback cuando se lanza una unchecked (Runtime) exception. Es decir, la mejor manera de controlar esto es crearse una exception en tu proyecto que extienda de Runtime Exception.
class BaseException extends RuntimeException { ... }
class HolidayService { static transactional = true def saveHoliday(holidayInstance){ ...intermediate saves before saving holiday... if (holidayInstance.save()) { return holidayInstance } else { throw new BaseException() } } }
class HolidayController { def holidayService def save(){ def holidayInstance = new Holiday(params) try{ def holidaySaved = holidayService.saveHoliday(holidayInstance) if(holidaySaved){ flash.message = message(code: 'default.created.message', args: [message(code: 'holiday.label', default: 'Holiday'), holidayInstance?.id]) redirect(action: "list") }else{ flash.errors = message(code: 'default.not.created.message', args: [message(code: 'holiday.label', default: 'Holiday'), holidayInstance?.id]) render(view: "create", model: [holidayInstance: holidayInstance]) } }catch(BaseException e){ flash.errors = e.getErrors() return render(view: "create", model: [holidayInstance: holidayInstance]) } } }- Si os veis mejor con las anotaciones de Spring, podeis usar @Transactional(rollbackFor=Exception.class), siempre seteando antes tu servicio con static transactional = false. Si necesitas tambien que alguna exception NO haga el rollback puedes utilizar @Transactional(noRollbackFor=AnotherException.class)
class HolidayService { static transactional = false @Transactional(rollbackFor=BaseException.class,noRollbackFor=AnotherException.class) def saveHoliday(holidayInstance){ ...intermediate saves before saving holiday... if (holidayInstance.save()) { return holidayInstance } else { throw new BaseException() } } }- Si quereis por algun caso especial poner una checked exception en vuestro codigo del Servicio, teneis que hacer lo siguiente ya que Spring por defecto no hace el rollback para las checked exception.
class HolidayService { static transactional = false @Transactional(rollbackFor=Exception.class) def saveHoliday(holidayInstance){ try{ ...intermediate saves before saving holiday... if (holidayInstance.save()) { return holidayInstance } else { return null } }catch(Exception e){ e.printStackTrace() } } }- Por defecto la anotacion @Transactional tiene los siguientes parametros:
1) propagation -> por defecto es PROPAGATION_REQUIRED.
2) isolation -> por defecto es ISOLATION_DEFAULT.
3) readOnly -> por defecto es false, es decir read/write.
4) timeout
NOTA: Mucho cuidado si usais el plugin de Audit Logging ya que con este plugin la transaccionalidad en los servicios fallara si teneis en el Config esta linea.
auditLog.transactional=trueLa solucion es ponerlo a false.