2010-01-21 10 views
6

Necesito mostrar mensajes personalizados en mi aplicación Spring 3.0. Tengo una base de datos con Hibernate y hay varias limitaciones. Tengo dudas sobre cómo se debe manejar el DataIntegrityViolationException de una buena manera. Me pregunto si existe una forma de mapear la excepción con un mensaje establecido en un archivo de propiedades, ya que es posible en la validación de Restricciones. ¿Podría manejarlo automáticamente de cualquier manera o tengo que atrapar esta excepción en cada controlador?Cómo manejar DataIntegrityViolationException en Spring?

Respuesta

7

El problema al mostrar mensajes fáciles de usar en el caso de una violación de restricción es que el nombre de la restricción se pierde cuando se traduce el ConstraintViolationException de Hibernate al DataIntegrityViolationException de Spring.

Sin embargo, puede personalizar esta lógica de traducción. Si usa LocalSessionFactoryBean para acceder a Hibernate, puede suministrarlo con un SQLExceptionTranslator personalizado (consulte LocalSessionFactoryBean.jdbcExceptionTranslator). Este traductor de excepciones puede traducir un ConstraintViolationException en su propia clase de excepción, conservando el nombre de la restricción.

4

El resorte 3 proporciona dos formas de manejar esto: HandlerExceptionResolver en beans.xml, o @ExceptionHandler en su controlador. Ambos hacen lo mismo: convierten la excepción en una vista para renderizar.

Ambos están documentados here.

2

trato a DataIntegrityViolationException en ExceptionInfoHandler, encontrando limitaciones DB ocurrencias en mensaje de causa raíz y la convierten en mensajes i18n través constraintCodeMap:

@ControllerAdvice(annotations = RestController.class) 
@Order(Ordered.HIGHEST_PRECEDENCE + 5) 
public class ExceptionInfoHandler { 

    @Autowired 
    private MessageSource messageSource; 

    private static Map<String, String> constraintCodeMap = new HashMap<String, String>() { 
    { 
     put("users_unique_email_idx", "exception.users.duplicate_email"); 
     put("meals_unique_user_datetime_idx", "exception.meals.duplicate_datetime"); 
    } 
    }; 

    @ResponseStatus(value = HttpStatus.CONFLICT) // 409 
    @ExceptionHandler(DataIntegrityViolationException.class) 
    @ResponseBody 
    public ErrorInfo conflict(HttpServletRequest req, DataIntegrityViolationException e) { 
    String rootMsg = ValidationUtil.getRootCause(e).getMessage(); 
    if (rootMsg != null) { 
     Optional<Map.Entry<String, String>> entry = constraintCodeMap.entrySet().stream() 
       .filter((it) -> rootMsg.contains(it.getKey())) 
       .findAny(); 
     if (entry.isPresent()) { 
      e=new DataIntegrityViolationException(
        messageSource.getMessage(entry.get().getValue(), null, LocaleContextHolder.getLocale()); 
     } 
    } 
    return new ErrorInfo(req, e); 
    } 
    ... 
} 

se puede simular en mi Java Enterprise training application añadiendo/usuario de edición con el correo duplicado o una comida con dateTime duplicado.

ACTUALIZACIÓN:

otra solución: usar Controller Based Exception Handling:

@RestController 
@RequestMapping("/ajax/admin/users") 
public class AdminAjaxController { 

    @ExceptionHandler(DataIntegrityViolationException.class) 
    public ResponseEntity<ErrorInfo> duplicateEmailException(HttpServletRequest req, DataIntegrityViolationException e) { 
     return exceptionInfoHandler.getErrorInfoResponseEntity(req, e, EXCEPTION_DUPLICATE_EMAIL, HttpStatus.CONFLICT); 
    } 
+0

¿Ha tenido una configuración diferente para conseguir que la restricción de usar? Mi restricción es algo como esto 'UK_6DOTKOTT2KJSP8VW4D0M25FB7_INDEX_4' – LTroya

+0

Hay varias formas de crear restricciones legibles: 1. En el script SQL inicial. 2. Generación automática de DB con el parámetro 'uniqueConstraints' en la anotación' @ Table', p. '@Table (name =" meals ", uniqueConstraints = {@UniqueConstraint (columnNames = {" user_id "," date_time "}, name =" meals_unique_user_datetime_idx ")})' – GKislin

Cuestiones relacionadas