[Spring Data JPA] JpaRepository save()

NHJ·2022년 2월 6일
0

SimplaJpaRepository.save(S entity) 메서드

@Transactional
@Override
public <S extends T> S save(S entity) {

	if (entityInformation.isNew(entity)) {
		em.persist(entity);
		return entity;
	} else {
		return em.merge(entity);
	}
}

JpaRepository의 save() 메서드는 새 entity를 추가할뿐만 아니라 업데이트를 위한 용도로 사용된다. save() 내부 구현을 보면 위와 같은 코드로 되어있는데 파라미터로 들어온 entity가 새로운 entity라면 persist() 메서드를 호출하고 아니라면 merge() 메서드를 호출한다.

참고로 persist() 메서드는 Transient상태의 entity를 Persistent상태로 바꿔주며 merge() 메서드는 Detached상태의 entity를 다시 Persistent상태로 바꿔준다.

그렇다면 entity가 새로운 entity인지는 어떻게 구분할까?
바로 entity 식별자의 상태이다. 식별자가 객체(Long, String)일 때는 null, 기본 타입(int, long)일 때는 0이면 새로운 entity(Transient)로 판단하고 아니면 Detached로 판단한다.

@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 가 관리하고 있으며 두 객체의 주소를 비교해보면 둘은 사실 같은 주소, 즉 같은 인스턴스임을 알 수 있다.

아래의 id가 있는 postUpdate 객체의 경우 merge()를 호출하게 된다.

이 때 postUpdateEntityManager 가 관리하고 있는 대상이 아니기 때문에 이 인스턴스의 값을 바꿔봐야 쿼리에 영향을 미치지 않지만 save()의 반환값인 updatedPost 의 값을 바꾸면 객체의 상태가 반영되므로 쿼리에 영향을 미치게 된다. 따라서 EntityManager가 관리하는 반환값의 객체를 사용하는것이 에러를 미연에 방지할 수 있다.


[참고]

profile
화이팅!

0개의 댓글