현재 나는 교육을 듣고있는데, 팀원들과 좌석 예매 프로그램을 간단하게 구현하고 있다.
동시에 해당하는 좌석에 예매가 몰리게 되었을때, 동시성 문제를 해결하기 위한 코드를 작성하는 중, 동시성 문제를 잘 알기 위해서는 트랜잭션에 대해서도 잘 알아야 할 필요성을 느껴 트랜잭션에 대해서도 많이 공부를 하고 다시 코드를 작성했다.
그 중 매우 이해가 안되는 것을 발견했다.

해당 코드는, login()메소드를 호출하게 된다. 그랬을때, 트랜잭션이 시작이 되며, NickName에 해당하는 유저를 디비에서 찾아오게 된다.
그리고 유저 디비를 찾아오지 못하게 되었을 때, forceJoin()메소드가 호출하게 된다. 그랬을 때, 스프링 트랜잭션 전파 속성의 default 값이 "REQUIRED" 이므로 이미 만들어진 트랜잭션에 참여하게 된다.
그리고 login() 메소드가 쭈욱 하다가 트랜잭션이 끝날때, flush()가 날라가면서 커밋이 일어나니까, 그때 insert 쿼리가 날라가는 줄 알았다..
flush의 조건
- 트랜잭션의 커밋이 일어날 때
- 직접적으로 flush()를 사용할 때
- JPQL을 사용할 때
이렇게 3가지로 Flush가 일어난다고 생각을 했기에, 트랜잭션이 끝나면 insert 쿼리가 날라가는 줄 알았다.

그러나, 로그를 보면, 처음 login()메소드를 접근했을때, email로 user을 select하고, 해당 유저가 없으면, 그 후에 forceJoin()메소드가 실행되는데, save()가 일어나면 쿼리가 영속성 컨텍스트의 쓰기지연 저장소에 저장이 되었다가, 트랜잭션이 끝나면 flush()가 일어나면서 Insert쿼리가 나갈 줄 알았으나, 그냥 forceJoin()메소드 종료 시, insert 쿼리가 날라간다... 이에 해답을 얻은 게 있었다.
save() 메서드의 내부 동작을 보면, 처음 접근 시 persist()를 호출하여 엔티티를 영속성 컨텍스트에 저장한다.

그럼 해당 하는 객체를 영속성 객체로 저장한다는 건데, 어떤 분이 이렇게 말씀해주셨다.

그렇다. 이때 영속성 컨텍스트가 영속 가능한 엔티티와 비영속 엔티티를 구분하는 기준은 주로 PK의 유무이다.

나는 유저의 pk전략을 이렇게 IDENTITY로 설정을했다.
이에 검색을 해보니,

pk를 IDENTITY로 설정을해두면, 해당 객체는 pk가 없기에, 
1차캐시에 등록을 할수가 없는 것이다. 그렇기에 persist()가 발생 시, pk가 없으니 flush()가 임의로 발생하면서, 디비에 insert쿼리가 날라가게 된다.
그 후, 디비에는 pk가 들어아게되어 1차캐시에 해당 객체를 영속성객체로 만들어주게 된다.
그래서 forceJoin()메소드에서 유저를 만들고 save()를 호출했을 때, 해당 하는 객체가 pk가 없으니 insert 쿼리가 날라간 것이다.
분명 pk전략이 IDENTITY가 아니였다면, 내가 생각한 대로 쓰기지연 저장소에 insert()가 차곡차곡쌓였다가, 트랜잭션이 끝날 시, flush()가 일어나서 디비에 저장이 되는 게 맞는 것이다.
-> 요약
IDENTITY 전략을 사용할 때는, 데이터베이스가 기본 키(PK)를 자동으로 생성한다. 이 경우, 엔티티가 영속성 컨텍스트에 저장될 때 PK 값이 설정되지 않으므로 insert 쿼리가 즉시 실행된다.
persist(): 이 메서드는 엔티티를 영속성 컨텍스트에 저장하며, 이때 엔티티는 비영속 상태에서 영속 상태로 전환된다. 하지만 IDENTITY 전략에서는 PK가 데이터베이스에서 생성되므로, PK가 없기 때문에 flush()가 호출되어 insert 쿼리가 데이터베이스로 전송된다.
영속성 컨텍스트와 IDENTITY 전략:
IDENTITY 전략을 사용할 경우, PK가 없기 때문에 1차 캐시에 엔티티를 등록할 수 없다. 따라서 flush()가 자동으로 호출되어 데이터베이스에 insert 쿼리가 발생하고, 그 후에 데이터베이스에서 생성된 PK 값이 엔티티에 설정된다.
이후, 데이터베이스에 PK가 설정된 엔티티는 영속성 컨텍스트의 1차 캐시에 등록되어 관리된다.다른 PK 생성 전략과의 비교:
IDENTITY가 아닌 다른 PK 생성 전략(예: SEQUENCE, TABLE)을 사용하면, PK는 엔티티가 영속성 컨텍스트에 저장될 때 미리 할당되지 않는다. 이 경우, flush()가 호출될 때까지 insert 쿼리는 지연 저장소에 쌓이다가, 트랜잭션 종료 시점에 데이터베이스에 저장된다.
결론적으로, IDENTITY 전략을 사용하는 경우, save() 메서드에서 persist()가 호출되면 PK가 없는 상태로 인해 flush()가 자동으로 발생하여 즉시 insert 쿼리가 실행된다. 이후 PK가 데이터베이스에서 생성되고, 엔티티는 1차 캐시에 등록되어 영속성 컨텍스트에서 관리된다.
참고