테스트 코드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()을 할 때는 캐시에 있던 인스턴스가 반환된 것입니다.
엔티티 매니저가 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
'개발 > JPA' 카테고리의 다른 글
DTO (0) | 2022.07.28 |
---|---|
jpa domain entity repository (0) | 2022.07.27 |
JPA *Repository , @Transactional, @Autowired , @Query, @Modifying, DI, JpaRepository-> findById , getById (0) | 2022.07.21 |
@EntityListeners @MappedSuperClass @CreatedDate @Column(updatable = false) 어노테이션 (0) | 2022.07.21 |
jpa envers 테이블 칼럼 열 의미 #REV , #REVTYPE, #Enum (0) | 2022.07.21 |