관리 메뉴

bright jazz music

[bootBoard] N:1(다대일) 연관관계: 6-1. @ManyToOne과 Eager loading 본문

Framework/Spring

[bootBoard] N:1(다대일) 연관관계: 6-1. @ManyToOne과 Eager loading

bright jazz music 2022. 9. 27. 20:00

두 개 이상의 엔티티 간의 연관 관계를 맺고 나면, 엔티티 클래스들이 실제 DB 상에서는 두 개 혹은 두 개 이상의 테이블로 생성된다. 따라서 연관관계를 맺고 있다는 것은 DB의 입장에서는 조인이 필요하다는 뜻이 된다.

 

실제로 @ManyToOne의 경우 FK 쪽의 엔티티를 가져올 때 PK 쪽의 엔티티도 함께 가져온다.

 

1. BoardRepositoryTests를 통해 Member를 @ManyToOne으로 참조하고 있는 Board를 조회하는 테스트 코드 작성

//BoardRepositoryTests.java

package com.example.bootboard.repository;

import com.example.bootboard.entity.Board;
import com.example.bootboard.entity.Member;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Optional;
import java.util.stream.IntStream;

@SpringBootTest
public class BoardRepositoryTests {

    @Autowired
    private BoardRepository boardRepository;

    @Test
    public void insertBoard(){
        IntStream.rangeClosed(1, 100).forEach(i -> {
            Member member = Member.builder().email("user" + i + "@aaa.com").build();

            Board board = Board.builder()
                    .title("Title..." + i)
                    .content("Content..." + i)
                    .writer(member)
                    .build();
            boardRepository.save(board);
        });
    }

	//테스트
    @Test
    public void testRead1(){
        Optional<Board> result =boardRepository.findById(100L); //DB에 존재한는 번호

        Board board = result.get();

        System.out.println(board);
        System.out.println(board.getWriter());
    }

}

/*
* testInsert()는 한 명의 사용자가 하나의 게시물을 등록하도록 작성되었다.
* */

테스트를 실행하면 쿼리가 내부적으로 left outer join 처리를 한다.

 

 

2. ReplyRepositoryTests를 통해 Board를 @ManyToOne으로 참조하고 있는 Reply를 조회하는 테스트 코드 작성

Reply와 Board 역시 @ManyToOne의 관계이므로 테스트 하면 자동으로 조인이 처리됨

//ReplyRepositoryTests.java

package com.example.bootboard.repository;

import com.example.bootboard.entity.Board;
import com.example.bootboard.entity.Reply;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import javax.swing.text.html.Option;
import java.util.Optional;
import java.util.stream.IntStream;

@SpringBootTest
public class ReplyRepositoryTests {
    @Autowired
    private ReplyRepository replyRepository;

    @Test
    public void insertReply() {

        IntStream.rangeClosed(1, 300).forEach(i -> {
            //1~100까지의 임의 번호
            long bno = (long)(Math.random() * 100) + 1;

            Board board = Board.builder().bno(bno).build();

            Reply reply = Reply.builder()
                    .text("Reply......" + i)
                    .board(board)
                    .replyer("guest")
                    .build();

            replyRepository.save(reply);
        });
    }
    
    //조인 테스트
    @Test
    public void readReply1() {
        Optional<Reply> result = replyRepository.findById(1L);
        
        Reply reply = result.get();

        System.out.println(reply);
        System.out.println(reply.getBoard());
    }
}

 

left outer join 확인

 

실행된 SQL을 보면 reply 테이블, board 테이블, member 테이블까지 모두 조인으로 처리된 것을 볼 수 있다.

Reply를 가져올 때 매번 Board와 Member까지 조인해서 가져올 필요가 많지는 않다. 따라서 위처럼 여러 테이블이 조인으로 처리된느 상황은 비효율적이다.

 

이와 같이 특정 엔티티를 조회할 때 연관관계를 가진 모든 엔티티를 함께 즉시 로딩하는 것을 Eager Loading(즉시 로딩)이라고 한다.

 

- Eager Loading은 연관관계가 있는 모든 엔티티를 한 번에 가져온다.

- 여러 연관관계를 맺고 있는 엔티티라면 로딩 시 필연적으로 성능이 저하된다.

- 즉시 로딩은 불필요한 조인까지 처리해야 하므로 되도록 사용하지 않는다.

 

즉시 로딩과 반대되는 개념으로 Lazy Loading(지연 로딩)이 있다. 성능을 고려한다면 지연로딩을 사용하는 편이 낫다.

Comments