지연 로딩(LAZY)과 즉시로딩(EAGER)
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "member_name")
private String name;
@ManyToOne(fetch = FetchType.EAGER)
//@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
private Team team;
}
- EAGER를 해둘 경우에는 연관관계가 걸려있는 엔티티를 모두 조회해서 실제 객체로 바인딩 됩니다.(Member만 조회해도 Team에 대한 쿼리까지 함께 나갑니다.)
- LAZY로 해둘 경우에는 연관관계에 있는 엔티티들이 우선 프록시 객체로 초기화됩니다.(member를 조회할 경우에는 member에 대한 쿼리만 나가고 실제 getTeam을 통해 호출시 쿼리가 날아갑니다.)
public Member find(Long id) {
Member member = memberRepository.findById(id).orElse(null);
// 이 시점에 member에 대한 쿼리가 나가고
Team team = member.getTeam();
// 이 시점에 team에 대한 쿼리가 나감
return member;
}
지연 로딩 시 직렬화 에러
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.jpatest.Member["team"]->com.jpatest.Team$HibernateProxy$qaGmdRth["hibernateLazyInitializer"])
Jackson 라이브러리는 기본적으로 이 프록시 객체를 json으로 어떻게 생성해야 하는지 모름
→ 예외 발생
해결방법
- Hibernate5Module 등록 → 기본적으로 초기화 된 프록시 객체만 노출, 초기화 되지 않은 프록시 객체는 노출 안함.
- EAGER로딩 사용 → 연관관계가 걸려있는 모든 엔티티들에 대해 객체를 초기화 시켜줌
- DTO를 이용해 원하는 값만 바인딩 시켜 노출
public MemberDto find(Long id) {
Member member = memberRepository.findById(id).orElse(null);
Team team = member.getTeam();
MemberDto build = MemberDto.builder().member(member)
.teamId(team.getId())
.teamName(team.getName())
.build();
return build;
}
N + 1문제
- 지연 로딩과 즉시 로딩 어떤 방법을 이용하더라도 연관관계 설정에 의해 N+1문제는 발생할 수 밖에 없다.
- memberRepository.findAll(); → member모두를 불러오는 쿼리를 날림 → 모든 member에 대해 team에 대한 쿼리가 추가로 발생
- member가 100만명이라면 member전체를 불러오는 쿼리 1개 + meber에 따른 team에 대한 쿼리 100만번 발생
해결책
- 임시방편 - LAZY로딩 필요할 때 가져오게 끔 막아두는 것입니다.(EAGER를 사용하면 안되는 이유)
- 실제 해결방법 - Fetch Join → 지연 로딩이 걸려있는 연관관계에 대해서 한번에 같이 즉시로딩해주는 구문입니다.
- @Query("select m from Member m left join fetch m.team") List<Member> findAllJPQL();
- 위와 같이 쿼리를 날리면 100만명의 member에대한 쿼리 하나로 해결이 가능합니다.
API 개발을 위한 JPA 2 - 1:1 ,N:1 성능 최적화
'Spring' 카테고리의 다른 글
Session을 이용한 로그인1(직접 구현) (1) | 2023.05.20 |
---|---|
세션고정 취약점 및 보호 정책 (0) | 2023.05.18 |
JWT인증 설정 시 SpringBootTest (0) | 2023.05.06 |
@preAuthorize() 로그인 페이지 매핑 (0) | 2023.04.28 |
@AllArgsConstructor 사용 지양에 따른 리팩토링 (1) | 2023.04.19 |