JPA Save()동작구조

Kyle_Kim·2022년 9월 26일
0

Save 메소드에 대한 심도 깊은 고찰

JpaRepository의 save()는 단순히 새 엔터티를 추가하는 메소드가 아니다.

save()는 업데이트를 위한 용도로도 사용될 수 있다.

Transient 상태의 객체라면 EntityManager.persist() Detached 상태의 객체라면 EntityManager.merge()

그러면 어떻게 Transient인지 Detached인지를 판단할 수 있을까?

먼저 Entity의 @Id 프로퍼티를 찾고 해당 프로퍼티가 null이면 Transient 상태로 판단하고 null이 아니면 Detached로 판단한다.

Entity 생명주기를 까먹었기 떄문에 다시 리마인드할 겸 정리해둔다

Transient : 새롭게 만들어진 객체. 하이버네이트나 DB 둘다 얘의 정보를 모름.

Persistent : 영속성이라고도 불리우는 상태. Persistent 컨텍스트가 관리를 하는 상태이다. 상태를 관리하거나 상태라면 필요한 경우 DB에 싱크를 한다.

Detached : 한 번이라도 DB에 Persistent가 되었던 객체이다. 즉 이 객체의 id가 테이블에 매핑이 되는 경우.

Detached.merge()의 오묘함

여기서 한 가지 알아둬야할 부분은 JPA에서 Detached상태는 merge()를 할 때 반드시 update query만 날리는 것은 아니다.

가령 해당 Entity가 DB 테이블에 존재하지 않는 경우라면 insert query를 날리기도 한다.

그러면 여기서 의문이 생길 수 있다.

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Import(PostRepositoryTestConfig::class)
class  PostRepositoryTest {

    @Autowired
    lateinit var postRepository:PostRepository

    @PersistenceContext
    lateinit var entityManager: EntityManager

    @Test
    fun entityLifeCycleTest() {
        val post = Post()
        post.title = "JPA"
        val savedPost = postRepository.save(post) // persist

        assertThat(entityManager.contains(post)).isTrue()
        assertThat(entityManager.contains(savedPost)).isTrue()
        assertThat(savedPost == post)

        val postUpdate = Post()
        postUpdate.id = post.id
        postUpdate.title = "Hibernate"
        val updatedPost = postRepository.save(postUpdate) // merge

        assertThat(entityManager.contains(updatedPost)).isTrue()
        assertThat(entityManager.contains(postUpdate)).isFalse()
        assertThat(postUpdate != updatedPost)

    }
    ...
}```

id가 없는 post 객체의 경우 Transient 상태에서 save가 될 경우 Persistent 상태로 변경된다.

이 때 save 메소드의 매개변수인 post 객체는 EntityManager에 캐싱된다.

이 때 save 메소드의 반환값인 savedPost 객체와 post 객체는 서로 동일한데, 그 이유는 save 메소드의 반환 값이 post 객체를 그대로 반환하기 때문이다.

그래서 savedPost와 post 모두 EntityManager가 관리하고 있으며 두 객체의 주소를 비교해보면 둘은 사실 같은 주소, 즉 같은 인스턴스임을 알 수 있다.

자 그러면 아래의 save는 왜 merge일까?

그건 당연히 postUpdate가 id값을 갖고 있기 때문이다.

앞서 이야기 했듯이, JPA는 Entity가 DB 테이블에 해당 도메인의 Id 값이 존재하지 않는 경우 Transient 상태로 판단한다.

그러나 밑의 postUpdate의 경우 post의 id를 갖고 있기 때문에 이 save 메소드는 EntityManager가 merge()를 호출하게 되는 것이다.
profile
Make Things Right

0개의 댓글