How to handle DataIntegrityViolationException in Spring? How to handle DataIntegrityViolationException in Spring? spring spring

How to handle DataIntegrityViolationException in Spring?


The problem with showing user-friendly messages in the case of constraint violation is that the constraint name is lost when Hibernate's ConstraintViolationException is being translated into Spring's DataIntegrityViolationException.

However, you can customize this translation logic. If you use LocalSessionFactoryBean to access Hibernate, you can supply it with a custom SQLExceptionTranslator (see LocalSessionFactoryBean.jdbcExceptionTranslator). This exception translator can translate a ConstraintViolationException into your own exception class, preserving the constraint name.


I treat DataIntegrityViolationException in ExceptionInfoHandler, finding DB constraints occurrences in root cause message and convert it into i18n message via constraintCodeMap:

@ControllerAdvice(annotations = RestController.class)@Order(Ordered.HIGHEST_PRECEDENCE + 5)public class ExceptionInfoHandler {  @Autowired  private final MessageSourceAccessor messageSourceAccessor;  private static Map<String, String> CONSTRAINS_I18N_MAP = Map.of(      "users_unique_email_idx", EXCEPTION_DUPLICATE_EMAIL,      "meals_unique_user_datetime_idx", EXCEPTION_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) {        String lowerCaseMsg = rootMsg.toLowerCase();        for (Map.Entry<String, String> entry : CONSTRAINS_I18N_MAP.entrySet()) {            if (lowerCaseMsg.contains(entry.getKey())) {                return logAndGetErrorInfo(req, e, VALIDATION_ERROR, messageSourceAccessor.getMessage(entry.getValue()));            }        }    }    return logAndGetErrorInfo(req, e, DATA_ERROR);  }  ...}

Can be simulated in my Java Enterprise training application by adding/editing user with duplicate mail or meal with duplicate dateTime.

UPDATE:

Other solution: use 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);    }


Spring 3 provides two ways of handling this - HandlerExceptionResolver in your beans.xml, or @ExceptionHandler in your controller. They both do the same thing - they turn the exception into a view to render.

Both are documented here.