우리가 단순히 저장을 위해 사용하던 ~~Repository.save(T Object)
이 코드는 Entity를 호출해 신규로 저장하거나 업데이트한다.
사실 이는 update, insert 두 쿼리를 구분해서 날린다는 사실 알고 있었나요?
?? 어?? 생각해본적 없는데..?
네 저도 생각해본적 없었는데..ㅎㅎ 갑자기 알게 됐슴둥..
예제를 보면서 함께 이해해봅시다.
SpringBoot Data JPA
@Test
public void SaveUpdateTest(){
Entity en = new Entity();
en.A = 1;
// 첫번째 save
entityRepository.save(en);
Entity en2 = entityRepository.findByA(1);
en2.A = 2;
entityRepository.save(en2);
assertThat(entityRepository.findByA(1)).isEqualTo(2);
}
위 코드를 봐보자
핵심은 Spring-Data-Jpa가 저 save() 인터페이스를 실행하게 되면 SimpleDataJpaRepository의 save를 호출한는데
이를 넘겨받은 SimpleDataJpaRepository는 EntityInformation type 필드 entityInformation을 갖고 있습니다. entityInformation에서는 Entity의 MetaData를 가지고 있어요.
내부에서 isNew에 Entity를 파라미터를 넘겨서 새롭게 생성된 Entity인지 검사합니다.
<SimpleDataJpaRepository.class>
@Transactional
@Override
publce<S extends T>S save(S entity){
Assert.notNull(entity,"Entity must not be null");
if(entityInformation.isNew(entity)){
em.persist(entity);
return entity;
}else{
return em.merge(entity);
}
}
여기 코드에서 보면
두가지 경우로 나뉘어서 보게 됩니다.
Version 필드 값이 null이라면 New Entity로 판단한다. Version 필드를 가진 Entity라면 Id 필드를 New Entity로 판단하는데 활용하지 않는다.
처음 우리 예제에서 처음 save 이전에는 Id 와 Version필드에 값이 없는 New Entity 상태이다. 그래서 SimpleJpaRepository.class 의 save에서 em.persist를 실행한다.
2번째 save에서는 id필드 (또는 Version) 에 값이 있는 Entity를 다시 save를 친다면 isNew는 false를 반환하고 em.merge()를 실행하게 된다.
EntityManager가 해당 Entity를 persist하면 Entity가 TRANSIENT 상태로 인식하고 Insert 구문을 실행한다.
EntityManager가 해당 Entity를 merge 로 Entity가 DETACHED 상태로 인식하여 Update구문이 실행된다.
그렴 persist면 insert? merge면 update? 이 구분은 hibernate의 EntityManager의 구현체 코드를 살펴봐야한다.
와!