지나공 : 지식을 나누는 공간

@Transactional에서 JPA로 delete한 뒤 insert가 안될 때, duplicate entry 에러가 날 때 해결하기 본문

우당탕탕 삽질기

@Transactional에서 JPA로 delete한 뒤 insert가 안될 때, duplicate entry 에러가 날 때 해결하기

해리리_ 2021. 5. 4. 18:01

일단 원인과 해결 방법부터 적고 내 사례와 해봤던 시도들을 구체적으로 적는 건 다음 포스팅으로 넘기려고 한다.

 

Spring Data JPA 사용 중에 데이터를 삭제한 뒤 추가하려고 했더니 duplicate entry 에러가 발생했다.

unique 조건이 있는 데이터인데 삭제가 되지 않은 채로 추가를 하려 해서 발생하는 에러였다.

(이게 중복 허용하는 데이터였고 디버깅을 제대로 안했다면 얼마나 큰 일이 났을런지 ...ㅎㅎ 상상하기 싫다.) 

 

실제 코드를 실행하는 부분에서 로그를 찍어보면 delete코드가 먼저 적혀있음에도 불구하고 hibernate가 insert를 먼저 실행하고 delete는 나중에 실행하는 걸 확인할 수 있다.

 

원인 : hibernate에서 동작하는 SQL 순서가 정해져 있기 때문.

 

실제 코드에서 flush() 하는 부분을 찾아 findUsage를 반복적으로 불러서 쩌어어어기 내부에 있는 메소드까지 계속 파고 들어가면 AbstractFlushEventListener.java에 있는 performExecution이라는 메소드가 실행된다는 것을 발견할 수 있다. 이 메소드와 관련된 hibernate의 레퍼런스를 찾아보면 아래와 같이 적혀 있다. 

 

Execute all SQL (and second-level cache updates) in a special order so that foreign-key constraints cannot be violated:

  1. Inserts, in the order they were performed
  2. Updates
  3. Deletion of collection elements
  4. Insertion of collection elements
  5. Deletes, in the order they were performed

이건 실제 레퍼런스 사진.

실행되는 우선순위가 Insert가 가장 먼저다. in special order!!!

 

구글링 하면 이런 실행 순서라고 나오는데 나는 실행된 메소드랑 레퍼런스를 직접 봐야 직성이 풀리는 스타일이라 flush 메소드가 실행될 때 호출되는 라이브러리 내부 메소드들을 다 파고들었고, break point를 두어 performExecution이 실행되는 걸 확인했다. 나처럼 직접 봐야겠는 분들은 포스팅 마지막 부분에 있는 다음 포스팅을 읽으시길!

 

레퍼런스 링크 : docs.jboss.org/hibernate/orm/4.2/javadocs/org/hibernate/event/internal/AbstractFlushingEventListener.html

 

AbstractFlushingEventListener (Hibernate JavaDocs)

protected  void performExecutions(EventSource session)           Execute all SQL (and second-level cache updates) in a special order so that foreign-key constraints cannot be violated: Inserts, in the order they were performed Updates Deletion

docs.jboss.org

 

어쨌든, 이게 주 원인이고 해결방안으로는 delete -> flush -> insert를 실행하는 것이 있다.

delete 코드를 작성한 후 이게 바로 DB에 반영되어 실제 데이터가 삭제되도록 flush를 한다. 그 이후에 insert가 되도록 한다.

 

 

delete 코드 이후 delete가 실제로 먼저 실행되도록 바로 flush()

 

flush를 통해 delete를 실제로 DB에 반영했으면 그 다음에 insert

 

 

 

글이 길어지니 개인적인 내 코드와 자세한 상황은 다음 포스팅에 이어서 작성하려고 한다.

아래 포스팅으로 들어가보자.

 

 

https://eocoding.tistory.com/94

 

@Transactional 분리 실험을 통해 트랜잭션 전파 유형과 Spring AOP 이해 / save와 saveAll()의 차이 제대로

이번 포스팅은 올해 5월에 올렸던 포스팅에 대한 후속 포스팅이다. https://eocoding.tistory.com/74 @Transactional에서 JPA로 delete한 뒤 insert가 안될 때, duplicate entry 에러가 날 때 해결하기 일단 원인..

eocoding.tistory.com

 

728x90
Comments