programing

Spring Boot에서는 org.hibernate는 처리되지 않습니다.예외.제약 위반예외.

magicmemo 2023. 3. 27. 21:09
반응형

Spring Boot에서는 org.hibernate는 처리되지 않습니다.예외.제약 위반예외.

엔티티 클래스에서 이메일을 검증하기 위한 패턴을 정의했습니다.검증 예외 핸들러 클래스에서 ConstraintViolation 핸들러를 추가했습니다.예외.어플리케이션에서는 Spring Boot 1.4.5를 사용하고 있습니다.

Profile.java

@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "profile")
public class Profile extends AuditableEntity {

  private static final long serialVersionUID = 8744243251433626827L;

  @Column(name = "email", nullable = true, length = 250)
  @NotNull
  @Pattern(regexp = "^([^ @])+@([^ \\.@]+\\.)+([^ \\.@])+$")
  @Size(max = 250)
  private String email;
....
}

확인Exception Handler.java

@ControllerAdvice
public class ValidationExceptionHandler extends ResponseEntityExceptionHandler {

  private MessageSource messageSource;

  @Autowired
  public ValidationExceptionHandler(MessageSource messageSource) {
    this.messageSource = messageSource;
  }

  @ExceptionHandler(ConstraintViolationException.class)
  public ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException ex,
  WebRequest request) {
    List<String> errors = new ArrayList<String>();
    ....
    }
} 

코드를 실행하고 유효하지 않은 이메일 주소를 전달하면 다음과 같은 예외가 발생합니다.handleConstraintViolation의 코드는 실행되지 않습니다.예외로 반환된 http 상태는 500이지만 400을 반환하고 싶습니다.내가 어떻게 그걸 할 수 있는지 알기나?

2017-07-12 22:15:07.078 ERROR 55627 --- [nio-9000-exec-2] o.h.c.s.u.c.UserProfileController        : Validation failed for classes [org.xxxx.common.service.user.domain.Profile] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
ConstraintViolationImpl{interpolatedMessage='must match "^([^ @])+@([^ \.@]+\.)+([^ \.@])+$"', propertyPath=email, rootBeanClass=class org.xxxx.common.service.user.domain.Profile, messageTemplate='{javax.validation.constraints.Pattern.message}'}]

javax.validation.ConstraintViolationException: Validation failed for classes [org.xxxx.common.service.user.domain.Profile] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
ConstraintViolationImpl{interpolatedMessage='must match "^([^ @])+@([^ \.@]+\.)+([^ \.@])+$"', propertyPath=email, rootBeanClass=class org.xxxx.common.service.user.domain.Profile, messageTemplate='{javax.validation.constraints.Pattern.message}'}]

at  org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:138)

at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:78)    

않다ConstraintViolationException.class코드 레이어에 전파되지 않기 때문에 하위 레이어에 의해 포착되어 다른 타입으로 래핑 및 재배열됩니다. 웹 는 ""가 .ConstraintViolationException.

경우에는 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★.TransactionSystemException 쓰고 @TransactionalJpaTransactionManager 때 예외는 트랜잭션에서 " " "로 됩니다."TransactionSystemException JpaTransactionManager.

다음과 같은 작업을 수행할 수 있습니다.

@ExceptionHandler({ TransactionSystemException.class })
public ResponseEntity<RestResponseErrorMessage> handleConstraintViolation(Exception ex, WebRequest request) {
    Throwable cause = ((TransactionSystemException) ex).getRootCause();
    if (cause instanceof ConstraintViolationException) {
        Set<ConstraintViolation<?>> constraintViolations = ((ConstraintViolationException) cause).getConstraintViolations();
        // do something here
    }
}

뭔가 덧붙이고 싶은 게 있어요.저도 같은 일을 하려고 했어요 실체를 확인하려고요그리고 컨트롤러의 입력을 확인해주면 스프링은 이미 모든 것을 즉시 사용할 수 있다는 것을 깨달았습니다.

@RequestMapping(value = "/profile", method = RequestMethod.POST)
public ProfileDto createProfile(@Valid ProfileDto profile){
...    
}

@Valid.validation javax.validation annotations를 .

프로파일 사용자 이름에 공백 공간을 허용하지 않는 regexp가 있는 패턴 주석이 있다고 가정합니다.

Spring은 상태 400(부정 요청)과 다음과 같은 바디로 응답을 작성합니다.

{
    "timestamp": 1544453370570,
    "status": 400,
    "error": "Bad Request",
    "errors": [
        {
            "codes": [
                "Pattern.ProfileDto.username",
                "Pattern.username",
                "Pattern.java.lang.String",
                "Pattern"
            ],
            "arguments": [
                {
                    "codes": [
                        "profileDto.username",
                        "username"
                    ],
                    "arguments": null,
                    "defaultMessage": "username",
                    "code": "username"
                },
                [],
                {
                    "defaultMessage": "^[A-Za-z0-9_\\-.]+$",
                    "arguments": null,
                    "codes": [
                        "^[A-Za-z0-9_\\-.]+$"
                    ]
                }
            ],
            "defaultMessage": "must match \"^[A-Za-z0-9_\\-.]+$\"",
            "objectName": "profileDto",
            "field": "username",
            "rejectedValue": "Wr Ong",
            "bindingFailure": false,
            "code": "Pattern"
        }
    ],
    "message": "Validation failed for object='profileDto'. Error count: 1",
    "path": "/profile"
}

다음 솔루션은 Spring Boot 2.1.2를 기반으로 합니다.

분명히 말씀드리면...nimai가 이미 올바르게 언급했듯이:

ConstraintViolation을 잡을 수 없습니다.Exception.class는 코드의 해당 계층에 전파되지 않기 때문에 하위 계층에 의해 포착되어 다른 유형으로 래핑 및 재배열됩니다. 따라서 웹 계층에 영향을 미치는 예외가 ConstraintViolation이 아닙니다.예외입니다.

, ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★」DataIntegrityViolationException이는 지속성 레이어의 문제를 지적합니다.하지만 그렇게 멀리 가게 놔두면 안 돼


솔루션

도 이렇게 하세요.@ValidEna가 언급한 방법 매개 변수로 지정된 엔티티에 대한 주석.내 버전에서는 이 버전이 누락되었습니다.org.springframework.web.bind.annotation.RequestBody주석(없음)@RequestBody에 주석을 달다ProfileDto에 올바르게 해석할 수 없습니다.ProfileDto엔티티와 속성으로 인해null예를 들어, 값입니다.NullPointerException.):

@RequestMapping(value = "/profile", method = RequestMethod.POST)
public ProfileDto createProfile(@Valid @RequestBody ProfileDto profile){
    ...
}

그러면 원하는 상태 코드 400과 기본 응답 본문이 함께 반환됩니다.org.springframework.web.bind.MethodArgumentNotValidException지속성 층에 도달하기도 전에 말이죠.의 처리MethodArgumentNotValidException에 정의되어 있습니다.org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler.

이것은 다른 토픽입니다만, 그 후, 그 동작을 덮어쓰기 할 수 있는 옵션이 있습니다.@ControllerAdvice와 함께@ExceptionHandler(MethodArgumentNotValidException.class)ErrorMvcAutoConfiguration을 제외할 경우 기본 오류 응답 본문이 최적이 아니며 존재하지도 않으므로 필요에 따라 응답 본문을 맞춤화할 수 있습니다.

주의:의 소재지@ExceptionHandler(MethodArgumentNotValidException.class)내부@ControllerAdvice를 확장하다ResponseEntityExceptionHandler결과물이 되다IllegalStateException왜냐하면ResponseEntityExceptionHandler이미 정의되어 있는 예외 핸들러MethodArgumentNotValidException그래서 그냥 다른 것에 넣으세요.@ControllerAdvice아무 것도 연장하지 않고 수업할 수 있습니다.


대체 수동 접근법

전자 메일 패턴의 유효성 검사를 수동으로 트리거할 수도 있습니다(수동으로 스프링 주석 유효성 검사 호출 참조).직접 테스트해 본 적은 없지만 개인적으로 이 방식은 컨트롤러 코드를 부풀리기만 할 뿐 현재 필요한 사용 사례가 떠오르지 않기 때문에 좋아하지 않습니다.

나는 그것이 비슷한 문제를 겪고 있는 다른 사람들에게 도움이 되기를 바란다.

ConstraintViolation을 잡을 수 없습니다.Exception.class는 코드의 해당 계층에 전파되지 않기 때문에 하위 계층에 의해 포착되어 다른 유형으로 래핑 및 재배열됩니다.따라서 웹 계층에 영향을 미치는 예외가 ConstraintViolation이 아닙니다.예외.다음과 같은 작업을 수행할 수 있습니다.

@ExceptionHandler({TransactionSystemException.class})
protected ResponseEntity<Object> handlePersistenceException(final Exception ex, final WebRequest request) {
    logger.info(ex.getClass().getName());
    //
    Throwable cause = ((TransactionSystemException) ex).getRootCause();
    if (cause instanceof ConstraintViolationException) {        

        ConstraintViolationException consEx= (ConstraintViolationException) cause;
        final List<String> errors = new ArrayList<String>();
        for (final ConstraintViolation<?> violation : consEx.getConstraintViolations()) {
            errors.add(violation.getPropertyPath() + ": " + violation.getMessage());
        }

        final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, consEx.getLocalizedMessage(), errors);
        return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
    }
    final ApiError apiError = new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, ex.getLocalizedMessage(), "error occurred");
    return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
}

다시 한 번 확인해 보겠습니다만,ConstraintViolationException

당신이 원하는 건org.hibernate.exception.ConstraintViolationException패키지.를 Import한 경우javax.validation.ConstraintViolationException경험하신 대로 건너뜁니다.

import org.hibernate.exception.ConstraintViolationException;

@RestController
public class FeatureToggleController {

    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException ex, WebRequest request) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
    }
}

이것은 예상대로 호출됩니다.

모든 예외를 선택하고 필요한 예외를 선택합니다.

  1. 원인을 특정할 필요가 있다:

    while ((cause = resultCause.getCause()) != null && resultCause != cause) {
        resultCause = cause;
    }
    
  2. 인스턴스 사용

    @ExceptionHandler(Exception.class)
    protected ResponseEntity<MyException> handleExceptions(Exception e) {
        String message;
        Throwable cause, resultCause = e;
        while ((cause = resultCause.getCause()) != null && resultCause != cause) {
            resultCause = cause;
        }
        if (resultCause instanceof ConstraintViolationException) {
            message = (((ConstraintViolationException) resultCause).getConstraintViolations()).iterator().next().getMessage();
        } else {
            resultCause.printStackTrace();
            message = "Unknown error";
        }
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(new MyException(message));
    }
    

그게 내 해결책이야

@ExceptionHandler({DataIntegrityViolationException.class})
    protected ResponseEntity<Object> handlePersistenceException(final DataIntegrityViolationException ex) {

        Throwable cause = ex.getRootCause();

        if (cause instanceof SQLIntegrityConstraintViolationException) {

            SQLIntegrityConstraintViolationException consEx = (SQLIntegrityConstraintViolationException) cause;

            final ApiErrorResponse apiError =  ApiErrorResponse.newBuilder()
                    .message(consEx.getLocalizedMessage())
                    .status(HttpStatus.BAD_REQUEST)
                    .build();

            return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
        }

        final ApiErrorResponse apiError =  ApiErrorResponse.newBuilder()
                .message(ex.getLocalizedMessage())
                .status(HttpStatus.NOT_ACCEPTABLE)
                .build();

        return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
    }
@ExceptionHandler(RollbackException.class)
   public ResponseEntity<ApiErrorsListResponse> handleNotValidException(RollbackException ex){

       String errMessage = ex.getCause().getMessage();

       List<String> listErrMessage = getListErrMessage(errMessage);
       ApiErrorsListResponse response = ApiErrorsListResponse.newBuilder()
               .status(HttpStatus.NOT_ACCEPTABLE)
               .errorMessage(listErrMessage)
               .build();

       return new ResponseEntity<>(response, HttpStatus.NOT_ACCEPTABLE);

   }

    public static List<String> getListErrMessage(String msg){

        Stream<String> stream = Arrays.stream(msg.split("\n"))
                .filter(s -> s.contains("\t"))
                .map(s -> s.replaceAll("^([^\\{]+)\\{", ""))
                .map(s -> s.replaceAll("[\"]", ""))
                .map(s -> s.replaceAll("=", ":"))
                .map(s -> s.replaceAll("interpolatedMessage", "message"))
                .map(s -> s.replaceAll("\\{|\\}(, *)?", ""));

        return stream.collect(Collectors.toList());
    }

public class ApiErrorsListResponse {

    private HttpStatus status;

 private List<String> errorMessage;

    public ApiErrorsListResponse() {
    }
...
}

이렇게 해보세요.

@ControllerAdvice
public class ControllerAdvisor extends ResponseEntityExceptionHandler {

    @Autowired
    BaseResponse baseResponse;

    @ExceptionHandler(javax.validation.ConstraintViolationException.class)
    public ResponseEntity<BaseResponse> inputValidationException(Exception e) {

        baseResponse.setMessage("Invalid Input : " + e.getMessage());
        return new ResponseEntity<BaseResponse>(baseResponse, HttpStatus.BAD_REQUEST);

    }
}

더해야 할 것 같아요.@ResponseStatus(HttpStatus.BAD_REQUEST)고객님께@ExceptionHandler:

@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException ex, WebRequest request) {
    List<String> errors = new ArrayList<String>();
    ....
}
@ResponseBody
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
@ExceptionHandler(DataIntegrityViolationException.class)
public Map errorHandler(DataIntegrityViolationException ex) {
    Map map = new HashMap();
    map.put("rs_code", 422);
    map.put("rs_msg", "data existed !");
    return map;
}

그냥 잡는다org.springframework.dao.DataIntegrityViolationException.

org.hibernate를 처리할 수 있습니다.예외.제약 위반@controllerAdvice에 추가함으로써 예외입니다.

@ExceptionHandler(DataIntegrityViolation)Exception.class) public ResponseEntity handleConstraintViolation예외(예외){

    String errorMessage = ex.getMessage();
    errorMessage = (null == errorMessage) ? "Internal Server Error" : errorMessage;

    List<String> details = new ArrayList<>();
     details.add(ex.getLocalizedMessage());

    return new ResponseEntity<ErrorResponseDTO>(
            new ErrorResponseDTO( errorMessage ,details), HttpStatus.INTERNAL_SERVER_ERROR);

}

Constraint Violation Constraint Violation 을 잡을 . 및 (「」를 포함)@ResponseStatus 따위를 써서 @ExceptionHandler(YourCustomException.class)Jpa Repository 。할 때는 .saveAndFlushmethods 에 、 DB 、 block block block block block블럭을 시도합니다.필요에 따라서, 다음과 같이 범용적으로 실시할 수 있습니다.

imports
...
public class ErrorHandler {

    public static <T> T execute(Supplier<T> repositorySaveFunction) {
        try {
            return repositorySaveFunction.get();
        } catch (DataIntegrityViolationException e) {
            if (e.getCause() instanceof org.hibernate.exception.ConstraintViolationException) {
                throw new CustomObjectAlreadyExistException();
            }
            if (e.getCause() instanceof PropertyValueException) {
                var fieldName = ((PropertyValueException) e.getCause()).getPropertyName();
                throw new CustomNotNullException(fieldName);
            }
            throw e;
        } catch (javax.validation.ConstraintViolationException e) {
            e.getConstraintViolations().forEach(constraintViolation -> {
                throw new CustomNotNullException(constraintViolation.getPropertyPath());
            });
            throw e;
        }
    }
}

서비스:

        imports
    ...
        @Service
        @Transactional
        public class Service {
        
            private final YourRepository yourRepository;
            ... constructor
        
            public ObjectToSave save(ObjectToSave objectToSave) {
                return execute(() -> yourRepository.saveAndFlush(objectToSave));
            }

        }

언급URL : https://stackoverflow.com/questions/45070642/springboot-doesnt-handle-org-hibernate-exception-constraintviolationexception

반응형