지연로딩(Lazy)? 즉시로딩(Eager)?
entity의 연관관계가 매핑된 상황을 가정하고 지연로딩과 즉시로딩을 간단하게 설명하면 다음과같습니다.
지연로딩 : 연관된 엔티티를 프록시로 조회한다. 프록시를 실제사용할때 (예를들어 getter호출) 초기화하면서 데이터 베이스를 조회합니다.
즉시로딩 : 연관된 엔티티를 즉시 조회합니다. 하이버네이트는 가능하면 SQL JOIN을 통해서 한번에 조회합니다.
그렇다면 어느것이 더 좋은것을까?
처음부터 연관된 엔티티를 모두 영속성 컨텍스트에 올려두는것은 현실적이지 않습니다. 산에가는데 꼭 모든 장비가 있어야하는것은 아닙니다. 때로는 물통한통만 있어도 되는것과 같습니다.
그럼 지연로딩이라고 무조건 좋은것일까? 한다면 최적화 관점에서보면 꼭 좋지만은 않습니다. 대부분의 애플리케이션 로직에서 연관관계의 객체그래프를 함께 사용한다면 이또한 IO가 많아지니 좋은 방법일수 없습니다.
제가 생각하기에 가장 좋은것은 등산을 할때 꼭 필요한것들은 EAGER로 항상 들고다니는 체크리스트에 들어가고 경우에 따라서 높은 산을 올라갈땐 지팡이를 챙긴다던지 겨울엔 아이젠을 챙긴다던지 하는 것과 같이 개발자가 비지니스를 잘 파악하여 상황에 맞게 Entity 설계를 하는것이 아닐까 생각합니다.
하지만! 관계가 복잡해지고 얽혀있을수록 즉시로딩으로 설정된 연관관계들 때문에 개발자가 전혀 의도치않은 쿼리가 보내지게 됩니다. 더불에 1+N문제를 가지고 있습니다. 그렇기 때문에 프로젝트가 커지고 관계가 복잡해질수록 지연로딩 (LAZY)를 선호합니다.
컬렉션에 EAGER 사용시 주의점
- 컬렉션을 하나 이상 즉시 로딩하는것은 권장하지 않습니다. 컬렉션과 조인한다는 것은 데이터베이스 테이블의 입장에서 보면 일대다 조인입니다. 일대다 조인은 결과 데이터가 다 쪽에 있는 수만큼 증가하게 됩니다. 문제는 서로 다른 컬렉션을 2개 이상 조인할때 발생하는데 예를 들어 A 테이블을 N,M 두 테이블과 일대다 조인을 하면 SQL실행결과가 N곱하기M이 됩면서 너무 많은 데이터를 반환할 수 있고 결과적으로 애플리케이션 성능이 저하될 수 있습니다. JPA는 이렇게 조회된 결과를 메모리에서 필터링해서 반환합니다. 그렇기때문에 권장하지 않습니다.
- 컬렉션의 즉시로딩은 무적권 외부조인 outer join을 사용합니다. 간단히 생각해보면 하이버네이트입장에서는 이 데이터가 있을지 없을지 알수 없는데 inner join을 하게되면 데이터를 들고 올 수 없게 되기 때문이지요.
그렇기때문에 연관관계설정시 중요하게 설정해야할 것이 optional 설정입니다.
@ManyToOne, @OneToOne
- optional = false : 내부조인
- optional = true : 외부조인
@OneToMany, @ManyToMany
- optional = false : 외부조인
- optional = true : 외부조인
김영한님의 JPA책에서 추천하는 방법은 모든 연관관계를 lazy로 설정한다음 완성단계에서 쿼리로그를보며 최소한의 부분만 EAGER로 설정하여 운영하는것입니다.
OneToOne에서 지연로딩
OneToOne의 N+1 문제를 해결하기위해서는 optional=false로 설정하여 엔티티가 무적권 있음을 보장하여 하이버네이트가 프록시객체를 당당하게 넣어줄수있게 설정을 해주는것입니다.
하지만 OneToOne자체를 권장하지않으며 OneToOne 연관관계를 맺는상황을 최대한 피해서 설계하는것이 좋은 설계가 될 수 있습니다. 불가피한 상황에서만 다음과 같이 설정하는것입니다.
@OneToOne(fetch = FetchType.LAZY, optional = false) // not null
'Springboot' 카테고리의 다른 글
Spring Data ElasticSearch NativeSearchQuery Paging 처리 (0) | 2022.12.07 |
---|---|
JPA update시에 dto null 체크및 feild 유무 판단하는 util class (0) | 2022.10.28 |
JDBCTemplate는 동시성문제를 개발자가 직접 해결해 줘야하나? (0) | 2022.10.21 |
Gradle 정리 (0) | 2022.10.20 |
@Transient 어노테이션 (0) | 2021.12.31 |