Spring Boot @ControllerAdvice + @Transactional не работает должным образом - PullRequest
0 голосов
/ 03 января 2019

Я работаю над сайтом ставок, поэтому транзакции очень важны.Я создал ExceptionHandler, используя @ControllerAdvice для перехвата всех исключений из бизнес-уровня.

@ControllerAdvice
public class HttpExceptionHandler {

  private String getStackTrace(final Throwable throwable) {
    final StringWriter stringWriter = new StringWriter()
    final PrintWriter printWriter = new PrintWriter(stringWriter, true)
    throwable.printStackTrace(printWriter)
    stringWriter.getBuffer().toString()
  }

  private List<String> filterStackTrace(final String stackTrace) {
    def stack = stackTrace.split('\n\t')
    stack.findAll({ it.contains('com.dsindigo.trading') })
  }

  private ResponseEntity<HttpErrorResponse> buildResponse(final Exception ex, final String message) {
    HttpErrorResponse error = new HttpErrorResponse(
      stack: filterStackTrace(getStackTrace(ex)),
      message: message
    )

    new ResponseEntity<HttpErrorResponse>(error, HttpStatus.BAD_REQUEST)
  }

  @ExceptionHandler(UsernameAlreadyExistsException)
  ResponseEntity<HttpErrorResponse> catchUsernameAlreadyExists(final UsernameAlreadyExistsException ex) {
    buildResponse(ex, HttpErrorMessages.USERNAME_ALREADY_EXISTS)
  }

  @ExceptionHandler(EmailAlreadyExistsException)
  ResponseEntity<HttpErrorResponse> catchEmailAlreadyExists(final EmailAlreadyExistsException ex) {
    buildResponse(ex, HttpErrorMessages.EMAIL_ALREADY_EXISTS)
  }

  //More exceptions...

  @ExceptionHandler(Exception)
  ResponseEntity<HttpErrorResponse> catchAny(final Exception ex) {
    buildResponse(ex, HttpErrorMessages.UNKNOWN)
  }
}

Поэтому в основном он перехватывает исключение (например, UsernameAlreadyExistsException) и создает ответ JSON, содержащий пользовательское сообщение иstacktrace (для целей отладки).

Это пример того, как служба генерирует пользовательские исключения:

@Service
class UserServiceImpl implements UserService {

  // @Autowired stuff ...

  @Override
  @Transactional
  UserDTO save(UserDTO user) {
    UserDTO current = findOneByUsername(user.username)

    if (current != null)
      throw new UsernameAlreadyExistsException()

    current = findOneByEmail(user.email)

    if (current != null)
      throw new EmailAlreadyExistsException()

    ConfigurationDTO configuration = configurationService.findOne();

    user.active = false
    user.password = bCryptPasswordEncoder.encode(user.password)
    user.balance = configuration.initialBalance

    User entity = mapper.map(user, User)
    entity = userRepository.save(entity)
    user = mapper.map(entity, UserDTO)

    transactionService.createForUser(user.id, INITIAL_CHIPS_ID, configuration.initialBalance)

    TokenDTO token = tokenService.createForUser(user.id)
    emailService.sendRegisterMessage(user.email, token.token)

    user
  }
}

Проблема заключается в том, что при создании пользовательского исключения без @Transactional обработчик исключенийвыполняет правильный метод, но добавление @Transactional всегда выполняет общий Exception метод.

Я что-то упустил?

1 Ответ

0 голосов
/ 03 января 2019

Эти объявления:

@ExceptionHandler(UsernameAlreadyExistsException)

Должно быть так:

@ExceptionHandler(UsernameAlreadyExistsException.class)

Кроме того, убедитесь, что ваши исключения расширяют RuntimeException.Если исключение поймано и обработано где-либо еще в вашем коде (включая перехватчики Spring!), Оно не будет обработано ControllerAdvice.

...