viernes, 1 de noviembre de 2013

Buenas practicas en Servicios

Muchas veces no hemos encontrado en la situacion de hacer un servicio que guarde las buenas practicas en Grails, en definitiva que guarde la transaccionalidad si ocurre una excepcion dentro de el.
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=true
La solucion es ponerlo a false.