JPA로 데이터베이스에 CRUD 작업을 수행할 때 발생할 수 있는 예외는 크게 JPA 자체 예외와 Hibernate 예외, 그리고 스프링 데이터 JPA에서 발생하는 예외로 구분할 수 있습니다.
- JPA 표준 예외
JPA 표준에서 정의된 예외는 javax.persistence 패키지에 속하며, 주로 다음과 같은 예외가 발생할 수 있습니다:
- EntityNotFoundException: 찾고자 하는 엔티티가 없을 때 발생합니다. 주로 find()나 getReference() 메서드를 사용하여 엔티티를 조회할 때 발생합니다.
- NoResultException: Query.getSingleResult() 메서드 호출 시 결과가 없을 경우 발생하는 예외입니다.
- NonUniqueResultException: 단일 결과를 기대했으나, 여러 개의 결과가 반환되었을 때 발생합니다.
- OptimisticLockException: 낙관적 잠금(Optimistic Lock)을 사용했을 때, 동시에 여러 트랜잭션이 하나의 엔티티를 수정하려 하면 발생하는 예외입니다.
- PessimisticLockException: 비관적 잠금을 사용하는 경우, 다른 트랜잭션에서 잠긴 데이터를 수정하려고 시도할 때 발생합니다.
- RollbackException: 트랜잭션 커밋 도중 발생한 예외로 인해 롤백이 발생했을 때 던져집니다.
- TransactionRequiredException: 트랜잭션이 필요하지만 존재하지 않을 때 발생합니다. 주로 flush(), persist(), merge()와 같은 메서드에서 발생합니다.
- Hibernate 예외
Hibernate는 JPA 구현체 중 하나로, Hibernate에 특화된 예외가 발생할 수 있습니다. 이러한 예외는 org.hibernate 패키지에 정의되어 있습니다:
- ConstraintViolationException: 데이터베이스에서 제약 조건(예: 고유 제약, 외래 키 제약 등)을 위반했을 때 발생합니다.
- DataException: 데이터베이스에서 데이터 타입이나 길이 제한을 위반했을 때 발생하는 예외입니다.
- JDBCConnectionException: 데이터베이스 연결이 실패했을 때 발생합니다.
- LazyInitializationException: 세션이 닫힌 후에 지연 로딩된 프록시 엔티티에 접근하려고 할 때 발생합니다.
- StaleObjectStateException: 낙관적 잠금 실패로 인해 엔티티 상태가 오래된 것으로 판정되었을 때 발생합니다.
- 스프링 데이터 JPA 예외
스프링 데이터 JPA는 JPA 및 Hibernate 예외를 추상화하여 스프링 프레임워크의 데이터 접근 계층에서 발생하는 예외로 변환합니다. 스프링의 데이터 예외는 org.springframework.dao 패키지에 속합니다:
- DataAccessException: 스프링의 모든 데이터 접근 예외의 기본 클래스입니다. 데이터베이스와 관련된 거의 모든 예외는 이 예외를 상속받습니다.
- EmptyResultDataAccessException: 쿼리의 결과가 없을 때 발생합니다. 주로 findById(), deleteById()에서 ID로 삭제하려 했으나 해당 엔티티가 없을 때 발생합니다.
- IncorrectResultSizeDataAccessException: 쿼리 결과가 기대한 것과 다른 크기를 가질 때 발생합니다. 예를 들어, 단일 결과를 기대했으나 여러 결과가 반환된 경우입니다.
- DuplicateKeyException: 고유 제약 조건을 위반하여 동일한 키를 가진 엔티티를 저장하려고 할 때 발생합니다.
- OptimisticLockingFailureException: 낙관적 잠금 실패로 인한 예외입니다.
- QueryTimeoutException: 쿼리 실행 시간이 초과되었을 때 발생합니다.
- 스프링 트랜잭션 예외
트랜잭션 관리와 관련된 예외도 발생할 수 있습니다:
- TransactionSystemException: 트랜잭션 도중 예상치 못한 시스템 오류가 발생했을 때 던져집니다.
- CannotCreateTransactionException: 트랜잭션을 생성할 수 없을 때 발생합니다.
- TransactionTimedOutException: 트랜잭션이 설정된 시간 안에 완료되지 않았을 때 발생합니다.
- 기타 예외
- IllegalArgumentException: 잘못된 매개변수가 전달되었을 때 발생합니다. 예를 들어, findById()에 null 값이 전달되었을 때 발생할 수 있습니다.
이와 같은 예외들은 JPA 기반의 CRUD 작업에서 자주 발생할 수 있으며, 적절한 예외 처리를 통해 시스템의 안정성을 높일 수 있습니다.
예외 종류
Unchecked 예외: RuntimeException 계열의 예외가 발생했을 경우, 별도로 예외 처리를 해주지 않으면 스프링 부트는 이를 500번 오류로 처리합니다.
Checked 예외: Exception 계열의 체크드 예외도 마찬가지로 예외 처리를 하지 않으면 500 상태 코드를 반환하게 됩니다.
예외처리
스프링 부트는 발생한 예외를 자동으로 ResponseEntityExceptionHandler에 전달하여 처리합니다. 만약 이 예외가 처리되지 않으면 사용자에게 기본적으로 500 Internal Server Error 응답이 반환됩니다.
따라서 예외처리를 통해서 사용자에게 적절한 응답을 해줘야 합니다.
예를 들어 TransactionTimedOutException발생시에는 단순하게 500번 토드를 반환하는것보다 400번 코드를 반환하고 "잠시후 다시 시도해주세요"라는 응답을 줄 수도 있다.