TestEntityManager

Alternative to EntityManager for use in JPA tests. 
Provides a subset of EntityManager methods that are useful for tests 
as well as helper methods for common testing tasks such as persist/flush/find.
JPA 테스트를 하기위한 대안으로써, 
엔티티매니저의 기본적인 메소드를 제공함과 동시에 테스트하기 유용하다. 
또한 헬퍼 메소드를 제공하는데 그에 따른 내용은 persist(), flush(), find() 등 이 존재한다.

스프링 부트 공식 독스

 

대략적으로 나는 아래와 같이 JPA 테스트를 시작한다.

@RunWith(SpringRunner.class)
@DataJpaTest
@EnableJpaAuditing
@Slf4j
public class AlbumJpaTest {

    @Autowired
    private TestEntityManager testEntityManager;

    @Autowired
    private AlbumRepository albumRepository;

    @Autowired
    private AlbumPhotoRepository albumPhotoRepository;

    @Autowired
    private AlbumCommentRepository albumCommentRepository;
    
    // 중략

}

 

TestEntityManager 에는 일반적으로 EntityManager 에 있는 메소드들을 다 제공해주고 있는 것 같다.

 

void clear()

  • EntityManager.clear() 와 동일
  • all managed entities to become detached, Clear the Persistence Context
  • 관리되고 있는 모든 엔티티들을 준영속 상태(detached)로 변경시킨다. 영속성 컨텍스트를 깨끗히 비운다.

void detach(Object entity)

  • EntityManager.detach(Object) 와 동일
  • a managed entity to become detached, Remove the given entity from Persistence Context
  • 영속성 상태에 있는 특정한 엔티티를 준영속 상태로 변경시킨다. 결과적으로 엔티티 매니저는 해당 엔티티를 관리하지 않는 상태에 두고 있다.

<E> E refresh(E Entity)

  • EntityManager.refresh(Object) 와 동일
  • overwriting changes made to the entity, Refresh the state of the instance from database
  • 데이터베이스에서 엔티티에 대한 상태값을 가져와서 덮어씌운다.

void flush()

  • EntityManager.flush() 와 동일
  • Synchronize the persistence context to the underlying database
  • 영속성 컨텍스트에 있는 엔티티의 변경내용을 가지고 데이터베이스와 동기화 시키는 작업이다. flush() 를 수행하면 데이터베이스에 영구적으로 반영된다.

void remove(Object entity)

  • EntityManager.remove(Object) 와 동일
  • Remove the entity instance
  • 영속성 컨텍스트에 있는 엔티티를 삭제하며, 데이터베이스에서도 해당 엔티티를 삭제한다.

<E> E merge(E entity)

  • EntityManager.merge(Object) 와 동일
  • Merge the state of the given into the current persistence context
  • 준영속상태에 있는 엔티티를 영속상태로 변경시킨다.

<E> E persist(E entity)

  • EntityManager.persist(Object) 와 동일
  • Make an instance managed and persistence, then return original source entity
  • 새롭게 만들어진 엔티티에 대해서 영속성 컨텍스트에 존재하게 하며, 엔티티 매니저에 의해 관리되게 한다. 이후 기본 엔티티의 값을 반환한다. 추가적으로 파라미터로 받은 엔티티 또한 영속성 컨텍스트에 포함된다.

<E> E find(Class<E> entityClass, Object primaryKey)

  • EntityManager.find(entityClass, primaryKey) 와 동일
  • Find by primary key.
  • 만약에 find() 메소드를 수행할 시 영속성 컨텍스트에 엔티티가 존재하면 데이터베이스에 select 쿼리를 날리지 않는다. 바로 영속성 컨텍스트 내에 존재하므로 거기서 바로 조회한다. 이를 1차 캐시 조회라고 부른다.

TestEntityManager 그리고 EntityManager 에 있는 내용들은 JPA 엔티티 상에서 연관관계 cascade = CascadeType 과 관련이 있다.

  • @OneToOne
  • @OneToMany
  • @ManyToOne
  • @ManyToMany

Cascade Type

  • cascade = CascadeType.PERSIST
    만약에 엔티티가 영속상태 또는 저장상태가 되었다면, 연관된 엔티티들도 영속상태가 된다.

  • cascade = CascadeType.REMOVE
    만약에 엔티티가 제거 또는 삭제되면, 연관된 엔티티들도 삭제 또는 제거 된다.

  • cascade = CascadeType.REFRESH
    만약에 엔티티가 리프레쉬되면, 연관된 엔티티들도 리프레쉬 된다.

  • cascade = CascadeType.DETACH
    만약에 엔티티가 디태치되면, not associated w/ session 이면 연관된 엔티티들도 디태치된다.

  • cascade = CascadeType.MERGE
    만약에 엔티티가 머지되면 연관된 엔티티들도 머지된다.

  • cascade = CascadeType.ALL
    PERSIST, REMOVE, REFRESH, DETATCH, MERGE 를 포함한다.

 

1차 캐시 조회

@Test
public void persistAndFindTest(){

    Album album = Album.builder()
            .content("콘택트 보고 댓글좀")
            .build();

    testEntityManager.persist(album);

    /**
     * 해당 내용이 존재 또는 미존재 하냐에 따라서 select 쿼리가 결정된다.
     * 왜냐하면 영속성 컨텍스트에 해당 엔티티의 내용이 존재여부가 쿼리 날리는 것이랑 연관이 있기 때문
     * **/
    testEntityManager.clear();

    testEntityManager.find(Album.class, 1L);
}

 

동일성 보장

@Test
public void persistAndFindAndFindTest_동일성_보장_테스트(){

    Album album = Album.builder()
            .content("컨택트 보고 댓글좀")
            .build();

    testEntityManager.persist(album);
    testEntityManager.clear();

    /**
     * find() 함수 두 번 호출
     * 그러나, 쿼리는 한 번 날라감
     * **/
    Album foundAlbum1 = testEntityManager.find(Album.class, 1L);

    Album foundAlbum2 = testEntityManager.find(Album.class, 1L);

    assertThat(foundAlbum1, is(foundAlbum2));
}
Hibernate: 
    select
        album0_.id as id1_0_0_,
        album0_.create_date as create_d2_0_0_,
        album0_.update_date as update_d3_0_0_,
        album0_.content as content4_0_0_ 
    from
        album album0_ 
    where
        album0_.id=?

 

글을 작성하다가 알게된 사항인데, 여기 링크에 매우 자세히 설명되어 있다. 나도 내용을 보면서 보충할 내용을 보충하는 것이 좋을 것 같다.

Posted by doubler
,