JPA Entity ID 생성 전략을 IDENTITY로 했을 때 일어나는 일

Sangwoon Park·2023년 4월 7일
0
post-thumbnail

배경

  • 생성 전략이 IDENTITY 면, flush 여부와 상관 없이 persist 즉시 INSERT SQL 이 DB로 날아간다.
  • 그럼 어디서 INSERT SQL을 Direct로 날리는가 궁금해졌다.

Dig Dig

https://github.com/ryanproback/jpa-id-gen-test

  • 테스트 환경을 구성해서 직접 트레이싱 해보았다.

환경

  • spring boot 2.7.10
  • mysql 8.0.22
  • hibernate 5.6.15

테스트 엔티티

@Getter
@Entity
@Table(name = "test_entity")
public class TestEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

}

테스트 코드

@PersistenceUnit
EntityManagerFactory entityManagerFactory;

@Test
public void test() {
	EntityManager entityManager = 
    	entityManagerFactory.createEntityManager();

	TestEntity entity = new TestEntity();

	entityManager.getTransaction()
                .begin();
	entityManager.persist(entity); // persist!

	assertThat(entity.getId()).isNotNull();

	entityManager.getTransaction()
                .commit();

	assertThat(entity.getId()).isNotNull();

	entityManager.close();
}

트레이싱

SessionImpl.persist()

EntityManager 의 구현체는 SessionImpl 이다. (Proxy 객체로 랩핑되어 있지만, 다루지 않는다.)
entityManager.persist() 호출시 SessionImpl에서 Override한 메소드가 호출된다.

DefaultPersistEventListener.onPersist()

fastSessionServices.eventListenerGroup_PERSIST.listeners 에 있는 리스너는 DefaultPersistEventListener 이다.

아직 Persist되지 않은 Entity의 상태는 TRANSIENT로 표현된다.
따라서 내부 메소드인 entityIsTransient 가 호출된다.
EntityState 판별은 여기서 다루지 않겠다.
대충 요약하면, 아직 PK가 존재하지 않기 때문에 TRANSIENT로 판별된다.

DETACHED : Persist된 적이 있고, 영속성 컨텍스트에 존재하지 않음.
PERSISTENT : Persist된 적이 있고, 영속성 컨텍스트에 존재함.
TRANSIENT : Persist된 적이 없으며, 영속성 컨텍스트에 존재하지 않음.

DefaultPersistEventListener.entityIsTransient()

createCache의 타입은 Map이다. createCache 는 onPersist에서 새로 생성된다.
아래와 같은 이유로 createCache.put은 null을 반환하므로 saveWithGeneratedId() 가 호출된다.

the previous value associated with key, or null if there was no mapping for key.

AbstractSaveEventListener.saveWithGeneratedId()

DefaultPersistEventListenerAbstractSaveEventListener를 상속하고 있다. saveWithGeneratedId()AbstractSaveEventListener 에 구현되어 있다.

AbstractSaveEventListener.performSaveOrReplicate()

AbstractSaveEventListener.addInsertAction()

source.getActionQueue().addAction(insert) 호출 시점에 DB로 INSERT SQL을 보낸다.
좀 더 파고들어가면 hibernate의 ActionQueue 에서 insert sql을 execute 하는 부분이 나오는데,
이쯤이면 궁금증은 충분히 풀어서 마친다 :)

profile
백엔드 개발자입니다.

0개의 댓글