티스토리 뷰
부제: 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; }
@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
'IT > 개발팁' 카테고리의 다른 글
[Spring Boot] RestTemplate 활용 1 - 응답 타입 일반화 (0) | 2020.01.19 |
---|---|
[Spring] 의존성주입(DI) 시 생성자 주입(Constructor Injection)을 써야하는 이유 (0) | 2019.10.12 |
Spring에서 request와 response를 JSON format 으로 한번에 로깅하기 (0) | 2019.03.24 |
[SpringBoot] .properties 파일의 상수를 Enum에 초기화하기 (0) | 2019.01.25 |
JAVA 제네릭(Generic) 문법 정리 (0) | 2018.08.05 |