일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- https
- 1*1000
- +9:00
- fractional seconds
- @CreateDate
- mysql equal null
- MSSQL
- getEntityGraph
- yml
- mysql not equal null
- mysql =
- 버전 문자열 비교
- EmbeddedId
- Protecle
- 티스토리챌린지
- AuditingEntityListener
- RootGraph
- pooled-lo
- apatch poi
- NamedEntityGraph
- 운동해서 광명찾자
- MYSQL
- @EntityListeners
- createEntityGraph
- load order
- getDateCellValue
- spring boot
- 오블완
- deserializer
- sendFractionalSeconds
- Today
- Total
Hello
[JPA] Service Layer 에서 DataIntegrityViolationException 처리 본문
유니크 컬럼에 중복값을 넣으려고 할 때, DataIntegrityViolationException이 발생해 db호출이 일어나는 메소드에서 Exception을 잡아 IllegalArgumentException으로 감싸 넘기려고 했는데 캐치 되지 않고 해당 에러가 리턴되었다.
@Transactional
public void addMemberPhone(MemberPhoneRequest memberPhone) {
MemberPhone memberPhone = new MemberPhone();
...
try{
memberPhoneRepo.save(memberPhone);
}catch (DataIntegrityViolationException e){
throw new IllegalArgumentException("이미 존재하는 전화번호 입니다.");
}
}
error
Exception이 캐치되지 않는 이유는 JPA의 영속성 컨텍스트와 관련이 있습니다.
- @Transactional이 붙은 메소드에서 Repository.save() or saveAll()이 호출될 때 쿼리를 실행하지 않고 쓰기 지연 상태가 되어 1차 캐시에만 쿼리를 보관 하고 있습니다.
- 메소드가 정상적으로 종료되었다면 실제 쿼리를 디비에 실행해 DB에 반영하게 됩니다.
- try-catch 블럭 내부에서 쿼리가 실행되지 않아 DataIntegrityViolationException이 발생되지 않습니다.
- 메소드가 종료된 후에 쿼리가 실행되면서 DataIntegrityViolationException이 발생합니다.
쓰기 지연 (transactional write-behind)
- 영속성 컨텍스트에 변경이 발생했을 때, 바로 DB로 쿼리를 보내지 않고 쿼리를 1차 캐시에 보관하고 있다가, 영속성 컨텍스트가 flush 하는 시점에 쿼리를 DB로 보냄.
해결방법
- saveAndFlush() 메소드 사용
- @ExceptionHandler 예외 캐치
- Controller 에 작성
- 프로젝트 전역 설정 필요한 경우 @ControllerAdvice
첫번째 방법
saveAndFlush() 메소드를 호출해 1차캐시에 쿼리를 보관하지 않고 DB에 바로 실행하도록 해 해당 메소드 내에서 try-catch 처리 가능
두,세번째 방법
작성 내용은 동일하되, 에러 캐치 범위에 대한 차이가 있습니다.
- Controller.class에 작성 하는 경우 클래스 내에서 발생한 DataIntegrityViolationException 만 캐치
- 프로젝트 전역에서 DataIntegrityViolationException이 발생한 경우 캐치
@ExceptionHandler(value = {DataIntegrityViolationException.class})
public ResponseEntity handleDataIntegrityViolationException(DataIntegrityViolationException e) {
log.error(e.getMessage(), e);
return ResponseEntity.badRequest().body(new IllegalArgumentException("중복된 데이터가 존재합니다."));
}
Service layer 단위 테스트 해당 이슈를 확인할 수 없음, Controller layer 에서 확인 필요함.
+)
@Transactional를 사용하지 않는 경우, 쿼리 실행 메소드마다 flush - commit 동작됨 트랜잭션기능을 사용할 수 없음.
사용하면 안되는 방법임.
참고 :
https://www.baeldung.com/spring-data-jpa-save-saveandflush
https://www.baeldung.com/spring-dataIntegrityviolationexception
'java' 카테고리의 다른 글
[Java]타임존 처리 ZonedDateTime + moment-timezone (0) | 2024.01.17 |
---|---|
[JPA] 하위 그래프가 하위 그래프를 사용하는 방법 (0) | 2024.01.11 |
[JPA] PK가 아닌 컬럼을 사용하여 연관관계 설정 (0) | 2024.01.03 |
점으로 구분된 버전 문자열 비교 (0) | 2023.12.06 |
[JPA] JPA Auditing이란 (+Spring Data JPA Auditing) (0) | 2023.11.17 |