Hansel
@OneToOne 매핑 주의사항 (Eager / Lazy) 본문
일반적으로 @OneToOne 혹은 ManyToOne의 어노테이션에서 주로 lazy 로딩을 사용한다.
eager를 사용하면 one으로 설정된 엔티티를 엔티티 조회시 함께 조회하기 때문에 해당 데이터가 필요하든 필요하지 않든쿼리가 +1 만큼 더 나간다.
따라서 Lazy 로딩을 통해 데이터가 필요할 때 해당 엔티티를 조회하거나 혹은 lazy 설정 후 fetch join으로 한번에 조회한다.
연관관계가 매핑이 지연로딩으로 설정된 엔티티는 조회시 그 객체가 아닌 일종의 가짜 객체인 프록시 객체를 가져온다.
해당 엔티티의 실제 값 참조가 일어날 때 프록시 객체 초기화가 이루어지며 영속성 컨텍스트에 요청을 한다.
이때 이미 fetch join으로 가져온 상태라면 따로 쿼리를 날리지 않는 것이고 그렇지 않다면 DB에 요청을 하면서 쿼리가 나간다.
그러니 주로 최적화하기 좋은 Lazy으로 설정한 뒤 적절히 사용한다.
하지만 Lazy로 설정했음에도 불구하고 eager 방식으로 쿼리가 나가는 경우가 있다.
위 사진은 메뉴(Menu), 댓글(Comment) + 작성자(Student), 가게정보(Seller) 등의 엔티티 정보만을 필요로 하는 화면이다.
하지만 왼쪽의 쿼리에선 뜬금없이 shopcart가 함께 나간다.
shopcart는 장바구니 객체인데 현재 Student와 oneToOne으로 매핑이 되어있고 양방향 관계이다.
회원이 가입함과 동시에 장바구니를 매핑해주려고 양방향으로 설정했는데 잘못된 선택이었다.
외래키는 shopCart가 관리하고 있다. (연관관계의 주인이 shopCart임)
프록시 객체 생성을 위해서는 매핑된 객체가 null인지 아닌지 확인이 필요하다.
shopCart의 경우 외래키를 관리하기 때문에 매핑된 객체가 null인지 아닌지 확인할 수 있다.
따라서 연관관계의 주인은 프록시 객체를 생성하여 지연 로딩이 가능해진다.
하지만 Student의 경우 외래키가 존재하지 않아 매핑된 객체를 직접 조회하여 null인지 아닌지 확인하는 절차를 거쳐야한다. 따라서 lazy 설정이 되어있음에도 불구하고 eager로 가져오게 되는것이다.
해결 방법은 대표적으로 2가지가 있다.
1. 양방향관계가 아닌 단방향으로 매핑한다.
2. fetch join을 사용해서 가져온다.
하지만 지금과 같은 경우 굳이 사용하지도 않을 데이터를 fetch join으로 가져와 쿼리문을 늘릴 필요가 없다.
따라서 단방향으로 설정한 뒤 유저가 회원가입을 하고 엔티티가 생성된 직후 장바구니 엔티티도 함께 생성하여 매핑시켜주는 방법으로 전환했다.
같은 상황에서 더 이상 shopCart 쿼리는 나가지 않는다.
'프로젝트 > 과정' 카테고리의 다른 글
QueryDsl 적용하기 1 (0) | 2022.07.31 |
---|---|
쿼리 분석 및 리팩토링 2 (0) | 2022.05.31 |
쿼리 분석 및 리팩토링 1 (0) | 2022.05.29 |
검색 구현(Pageable, Thymeleaf) (0) | 2022.05.25 |
회원가입 과정 변경하기 / Ajax, RestController, 카카오API (0) | 2022.05.23 |