티스토리 뷰

반응형

부제: Hibernate Dynamic Eager and Lazy Load. JPA eager/lazy load. JPA eager load 하기


 Hibernate는 JPA의 대표주자라고 할 수 있는 ORM(Object Relational Mapping)이다. MyBatis와 같은 SQL-Mapper와 달리 개발자가 쿼리를 직접 작성하지 않는다는 특징이 있다. 그래서 생산성이 좋고 SQL을 직접 작성하지 않으니 쓸데없이 DAO를 찍어낼 필요도 없고 장점이 많지만, 이 글에서 다루지 않을 것이다.




1. Eager load 와 Lazy load

  Eager-load와 Lazy-load에 대해 먼저 짚고 넘어가자. 뒤따라올 두개의 모델 예제를 보면 된다. 


@Entity
public class Student {

    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String name;

    private String gender;

    @ManyToOne
    private School school;
}

@Entity
public class School {

    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String name;

    private String major;

    @OneToMany
    private List<Student> studentList;
}

 Hibernate를 써본 적이 있다면 School과 Student는 1:N 관계임을 알 수 있다. 이 때 '서울대학교'의 전체 학생을 구하는 쿼리는 어떻게 작성할까? 예제를 계속 째려보자.



@Controller
public class PreamtreeController {

    @Autowired
    private SchoolRepository schoolRepository;

    @RequestMapping("/")
    public ResponseEntity<List<Student>> getAllStudents() {

        School school 
                = schoolRepository.getSchoolByName("서울대학교"); // School 만 조회
        List<Student> result 
                = school.getStudentList(); // 뒤늦게 Student 들을 Select 하는 쿼리가 Lazy 하게 전송됨.

        return new ResponseEntity<>(result, HttpStatus.OK);
    }
}


 별생각 없이 작성하면 위와 같이 작성하게 된다. 아니, 우리는 객체지향 모델에서 보통 이렇게 조회한다. 그런데 이렇게 조회하면 SELECT 쿼리가 두 번(school 조회 1회, Student 조회 1회. 총 2회) 전송된다. Hibernate는 기본적으로 Lazy loading을 채택하기 때문이다. 그러면 이제 Eager-load와 Lazy-load 에 대해 어느 정도 감이 왔을 것이다. 정의를 내리면 아래와 같다.


Eager-load: SELECT 연산 시 관계(Relation)이 있는 모든 테이블을 Join하여 load 한다.


Lazy-load: SELECT 연산 시 관계(Relation)이 있는 테이블을 무시한다.


 그러면 Join을 활용하여 한번에 조회하는 방법이 분명 있을 것이고, 그것이 Eager-load일 것이다. Eager-load를 설정하려면 모델 선언 시 애노테이션을 쓰면 된다.


@Entity
public class School {

    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String name;

    private String major;

    @OneToMany(fetch = FetchType.EAGER)
    private List<Student> studentList;
}



2. 동적으로 하는 것은 복잡하다..

 이제 제목으로 돌아가자. 이 글에서는 동적으로 Eager-load와 Lazy-load가 가능한지 알아볼 것이었다. 더 자세히 말하자면 Runtime에 특정 학교에 속한 학생 정보가 필요할 때는 School 테이블과 Student 테이블을 Join하여 조회하고 학교에 대한 정보만 필요할 때는 School 테이블만 조회(load)하는, 즉 상황에 따라 자유자재로 조회하는 것이 가능하냐는 것이다.


 결론부터 말하면 여러 가지 방법이 있다. 이 중 내가 선호하는 방식은 링크의 답변 중 첫번째에 소개된


 모든 관계를 Lazy load로 선언하고 Eager load가 필요하면 HQL을 활용하기


이다. 완벽하게 동적으로 하는 것은 아니지만, Eager-load와 Lazy-load를 모두 지원할 수 있고 Model을 따로 2개 선언하는 것보다 소스코드의 양이 줄어든다는 장점이 있다. 그리고 링크에 소개된 다른 방법들(Criteria를 설정하거나 동적 Profile을 설정하는 방법)은 너무 복잡하다. 즉, 이 방법은 단순하다는 장점이 있다.


한편, 모든 관계를 Eager-load로 설정하지 않는 이유는 Lazy-load를 사용할 일이 더 많기 때문이다. 마무리로 School, Student 모델에 어떻게 적용하는지 알아보자. HQL을 사용할 것이니 Repository 쪽만 변경해주면 된다.


public interface SchoolRepository extends JpaRepository<School, Long> {
    // Lazy loading
    School getSchoolByName(String name);

    // Eager loading by HQL
    @Query("SELECT a FROM School s join fetch s.studentList WHERE s.name=?1")
    School getSchoolByNameEagerLoad(String name);
}


-끝-




그림 출처: https://www.javatpoint.com/hibernate-tutorial





«   2021/12   »
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
글 보관함
Total
759,125
Today
15
Yesterday
253