IT'S DO
article thumbnail
728x90

테스트 코드1 : 같은 트랜잭션 내에서의 1차 캐시


@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional // test에서 @Transactional을 사용하면 자동 롤백된다.
public class MyshopApplicationTests {

   @Autowired //테스트할 클래스를 오토와이어로 주입
   CategoryRepository categoryRespository;

   @Autowired
   BoardRepository boardRepository;

   @Test
   public void contextLoads() {
   }

   @Test
   public void test1(){
      Category category=categoryRespository.getOne(1L);
      System.out.println(category.getId());
      System.out.println(category.getName());
      Category category2 = categoryRespository.getOne(1L);
      if(category==category2){
         System.out.println("category==category2");
      }

 

위의 test1()의 결과는 다음과 같다.

 

이상하지 않나요?? 분명히 getOne() 이라는 메서드를 2번 썼는데, 실제로 쿼리는 1번만 작성되었어요.

그 이유는 같은 트랜잭션 내부에서는 1차 캐시가 적용되기 때문입니다. category 인스턴스가 persistence context의 1차 캐시에 저장되어 있어서 2번째 getOne()을 할 때는 캐시에 있던 인스턴스가 반환된 것입니다.

 

1차 캐시란?

엔티티 매니저가 persist() 또는 find()를 하면 그 엔티티는 managed 상태가 되면서 persistence context의 1차 캐시에 저장이 됩니다. 1차 캐시는 일종의 map으로 생각하면 되는데 key는 @Id이고, value는 엔티티 인스턴스가 된다.

 

 

테스트 코드2 : 프록시 객체와 쿼리 실행 시점


@Test
public void test2(){
   System.out.println("------------------------------");
   System.out.println(categoryRespository.getClass().getName()); //프록시 객체 출력
   Category category = categoryRespository.getOne(1L); //Category 쿼리문 실행
   List<Board> boards=category.getBoards(); //Board 쿼리가 실행되지 않았다.
   System.out.println("------------------------------");
   System.out.println(boards.getClass().getName()); //PersistenceBag
   System.out.println("------------------------------");

   for(Board board : boards){ //실제로 Board 쿼리가 실행된 시점
      System.out.println(board.getTitle());
   }
   System.out.println("------------------------------");


위의 테스트 코드 결과는 다음과 같다

 

쿼리가 실행되지 않고, getTitle 할 때 쿼리가 실행된 이유?? 이건 선생님께 다시 질문

 

 

 

 

 

테스트 코드 3 : 쿼리 메서드를 이용해서 값 가져오기



@Test
public void test3(){
   List<Board> list=boardRepository.findAllByName("kim");
   //System.out.println(list);
   for(Board board : list){
      System.out.println(board.getTitle());
   }
}

실제로 findAllByName 이라는 메서드는 없다. 하지만 BoardRepository에서 해당 메서드를 만들 때 메서드의 이름이 자동완성 되는 것을 알 수 있다. 그 이유는 Spring Data JPA가 메서드 이름으로 쿼리문을 만들기 때문이다. 그것이 바로 "Query Method"이다.

 

public interface BoardRepository extends JpaRepository<Board, Long> {
    public List<Board> findAllByName(String name);

}

 

결과는 다음과 같다.

 

 

 

 

테스트 코드 4 : 1+N문제 코드와 JPQL



@Test
public void test4(){
   List<Board> list1=boardRepository.findAll(); //이것은 1+N문제를 발생시킨다.
   List<Board> list2=boardRepository.getBoards();//쿼리가 1번만 실행된다. 조인한 결과를 가져올 수 있다.

   for(Board board : list2){
      System.out.println(board.getTitle());
      System.out.println(board.getCategory().getName());
   }


}

 

쿼리 결과문

 

노란색 사각형이 1+N 쿼리를 실행하는 부분이고

연두색 사각형이 JPQL을 사용하여 쿼리를 1번만 실행시키는 부분이다.

 

밑에 출력문은 JPQL을 이용하여 출력한 결과이다.

 

그럼 1+N 문제는 무엇일까?  1+N 문제   <--- 여기 링크에서 확인하라

 

 

https://jhkang-tech.tistory.com/64

 

[Spring Boot] 엔티티 맵핑과 테스트 코드 작성

안녕하세요 강정호입니다. 오늘을 스프링 부트에서 엔티티를 맵핑하는 방법과 테스트 코드 작성에 대해 알아볼게요. 이렇게 Category와 Board는 다음과 같이 1:N 관계를 맺고 있습니다. 1개의 카테고

jhkang-tech.tistory.com

 

profile

IT'S DO

@멋진놈

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!