springframework/Spring Data JPA

Spring Data JPA : Enity 저장

Jungsoomin :) 2020. 11. 18. 18:43

JpaRepositorysave()단순히 Entity 를 추가하는 메서드가 아니다.

  • Transient 상태의 Entity 라면 persist() 를 호출하여 Persistent 상태로 변환한다.
  • Detached 상태의 Entity 라면 merge() 를 호출하여 Attach 시켜 Persistent 상태로 변환한다.

많은 배움을 주시는 백기선 강사님의 Spring Data JPA 강좌.


EntityManager.merge()

  • Merge() 메서드 의 매개 값인 Entity 의 복사본을 만들고, 복사본을 Persistent 상태로 돌린 후 리턴한다.
  • 만약, Id 식별자가 저장된 데이터에 없는 값이라면 새로운 Entity 로 간주하고 Insert 시킨다.

판단 기준은 @Id 프로퍼티이다.

  • @Id 프로퍼티가 null = Transient 상태로 간주
  • @Id 프로퍼티가 null이 아니다 = Detached 상태로 간주
@RunWith(SpringRunner.class)
@DataJpaTest
class PostRepositoryTest {

    @Autowired
    private PostRepository postRepository;

    @PersistenceContext
    EntityManager entityManager;

    @Test
    public void test(){
        Post post = new Post();
        post.setTitle("JPA");
        post.setCreated(new Date());

        Post savedPost = postRepository.save(post); // Transient > Persistent = persist() 호출
        assertThat(entityManager.contains(post)).isTrue();
        assertThat(entityManager.contains(savedPost)).isTrue();
        assertThat(post == savedPost).isTrue();

        Post update = new Post();
        update.setId(savedPost.getId());
        update.setTitle("Hibernate");
        update.setCreated(new Date());

        Post updatedPost = postRepository.save(update); // Detached > Attach = merge() 호출
        assertThat(entityManager.contains(updatedPost)).isTrue();
        assertThat(entityManager.contains(update)).isFalse(); // merge() 호출 시 복사 본만 Persistent 상태로 돌린다는 것을 알 수 있다.
        assertThat(update == updatedPost).isFalse();

        List<Post> all = postRepository.findAll();
        assertThat(all.size()).isEqualTo(1);
    }
}

습관적인 쿼리 파악, 중간에 insert 가 불필요하게 반복되는 일이 있어서 쿼리를 계속 지켜보면서 원하는 결과가 도출되는지 Assertion 해보다가 프로젝트를 새로 만들고 시도했다.

Transient > Persistent => save() : Insert 문

Detached > Attach > Persistent => merge() : Update 문

Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    insert 
    into
        post
        (created, title, id) 
    values
        (?, ?, ?)
2020-11-18 18:28:47.911 TRACE 3692 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [TIME] - [Wed Nov 18 18:28:46 KST 2020]
2020-11-18 18:28:47.933 TRACE 3692 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [VARCHAR] - [JPA]
2020-11-18 18:28:47.948 TRACE 3692 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [3] as [BIGINT] - [1]
Hibernate: 
    update
        post 
    set
        created=?,
        title=? 
    where
        id=?
2020-11-18 18:28:47.963 TRACE 3692 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [TIME] - [Wed Nov 18 18:28:47 KST 2020]
2020-11-18 18:28:47.964 TRACE 3692 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [VARCHAR] - [Hibernate]
2020-11-18 18:28:47.964 TRACE 3692 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [3] as [BIGINT] - [1]
Hibernate: 
    select
        post0_.id as id1_0_,
        post0_.created as created2_0_,
        post0_.title as title3_0_ 
    from
        post post0_
2020-11-18 18:28:47.979 TRACE 3692 --- [           main] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([id1_0_] : [BIGINT]) - [1]

 

save() 호출 시 내부적으로 persist() , merge() 를 판단한다.

  • 차이점이 존재하나, 공통점리턴받은 Entity 객체모두 Persistent 상태로 관리된다는 점.
  • save 호출 시에는 리턴 받은 Entity 객체를 사용하는 것이 좋다.