프로그래밍/JPA
JPA(hibernate) INSERT, UPDATE, DELETE 순서 주의사항
승민아
2024. 2. 24. 17:27
문제가 발생한 상황은 다음과 같다.
회원은 프로필 이미지를 하나만 가질 수 있는 상황이다.
Member
@Entity
@Data
public class Member {
...
@OneToOne(mappedBy = "member", cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
private MemberImage memberImg;
...
}
MemberImage
@Entity
@Data
@NoArgsConstructor
public class MemberImage {
...
@OneToOne
@JoinColumn(name = "member_id")
private Member member;
}
@OneToOne으로 회원과 프로필 이미지는 하나만 가지도록 했다.
그리고 프로필 이미지 변경을 위해
기존 프로필 이미지를 DELETE -> 새로운 이미지를 INSERT 하는 과정을 코드로 작성했다.
위의 코드를 실행시 다음과 같은 에러가 발생한다.
2024-02-22T17:23:06.253+09:00 ERROR 19152 --- [io-8080-exec-11] o.h.engine.jdbc.spi.SqlExceptionHelper : Duplicate entry '2' for key 'member_image.UK_ho4xw7rdwutyabcwmwmg2ndu3'
중복된 키가 2개 존재해서 발생한 Unique 제약 에러 같다.
즉, member_image에 회원의 이미지는 하나만 존재해야하는데 2개가 존재해버리니 에러가 발생한 것이다.
원인은 hibernate의 쿼리 실행 순서이다.
JPA는 트랜잭션 내에서 동작한다.
쓰기 지연을 통해 트랜잭션을 커밋하기전까지 내부 쿼리 저장소에 모아놨다가
트랜잭션을 커밋할때 flush하여 모아둔 쿼리를 날린다.
쿼리의 우선 실행 순서는 다음과 같다.
- Inserts, in the order they were performed
- Updates
- Deletion of collection elements
- Insertion of collection elements
- Deletes, in the order they were performed
DELETE보다 INSERT가 우선이라 위의 @OneToOne의 UNIQUE 제약 조건에 걸리는 것이다.
DELETE후 INSERT를 하는 코드를 작성했더라도
쿼리 저장소에 모아놨다가 쿼리를 날리기에 우선순위에 따라 INSERT가 먼저 수행되어 에러가 발생한 것이다.
즉, 삭제 코드 후에 flush하여 삭제 쿼리를 먼저 날리도록 작성하고 INSERT 쿼리를 날리도록 하자.
위와 같이 repository의 flush() 메서드를 호출하여 쿼리를 미리 반영하자.