일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- jar빌드
- MySQL
- 저장소 복제
- JPA
- 넘파이
- 서버 배포
- Chat GPT
- JPQL
- 스프링부트 OpenAI API
- MVC
- 비밀번호 재설정 API
- 두수의 합 자바
- Git
- 자바
- 파이썬
- 우분투
- git 충돌 해결
- 프로그래머스
- api 개발
- 저장소 이전
- springboot
- Servlet
- 스프링 이메일 전송
- Json 객체
- swap 메모리
- github 복제
- 값 타입
- HttpServletResponse
- 페이징 정렬
- JDBC
- Today
- Total
현의 개발 블로그
[프록시와 연관관계 관리] 즉시로딩과 지연로딩 본문
서론
Member를 조회할 때 Team도 조회해야 할까?
Member만 사용하는데 연관관계가 걸려있다고 매번 Team도 함께 조회하면 낭비이다.
JPA는 지연로딩을 사용해 프록시 객체로 조회하여 이 문제를 해결한다.
지연로딩
Member와 연관관계 있는 TEAM을 지연로딩으로 설정한다.
//Member.java
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;
Team과 Member를 각각 저장하고 member에 team 참조를 넣어주었다.
Team team = new Team();
team.setName("teamA");
em.persist(team);
Member member1 = new Member();
member1.setUsername("member1");
member1.setTeam(team);
em.persist(member1);
Member m = em.find(Member.class, member1.getId());
Team을 프록시 객체로 조회하고 Member 객체만 DB에서 조회한다.
System.out.println("m = " + m.getTeam().getClass());
Team은 프록시 객체인 것을 확인할 수 있다.
m.getTeam().getName();
프록시를 강제 초기화를 해야 DB에서 Team을 조회하는 쿼리가 나간다.
즉시로딩
Member와 Team을 자주 함께 사용한다면, Member를 조회했을 때 Team 객체도 가져오는 게 좋다.
조인으로 한 번에 쿼리를 보내는 게 각각 쿼리를 보내는 것보다 효율적이기 때문이다.
즉시로딩을 사용하기 위해 어노테이션을 @ManyToOne(fetch = FetchType.EAGER) 로 바꿔준다.
지연로딩 때와 다르게, 조인으로 Member와 Team이 한 번에 조회한다.
Team 클래스를 출력해 보면, Team은 프록시가 아닌 진짜 객체이다.
m.getTeam().getName(); 을 해도, 프록시 객체가 아니므로 초기화할 일이 없기 때문에 쿼리가 나가지 않는다.
프록시와 즉시로딩 주의
가급적 지연로딩만 사용하자.
즉시 로딩을 적용하면 예상하지 못한 SQL이 발생한다.
즉시 로딩은 JPQL을 작성하면 N+1 문제를 일으킨다.
-> 처음 쿼리 하나를 날렸을 때 N개의 추가 쿼리가 발생한다.
@ManyToOne, @OneToOne은 기본이 즉시 로딩이므로 지연로딩으로 변경해야 한다.
@OneToMany, @ManyToMany는 기본이 지연 로딩이므로 변경할 필요 없다.
N+1문제
N+1문제는 JPQL로 하나의 쿼리를 날렸는데, 그로 인해 N개의 추가 쿼리가 발생하는 것이다.
실무에서는 복잡한 쿼리를 작성하기 위해 JPQL을 많이 사용한다.
기존에 배웠던 em.find()는 PK를 정해놓고 DB에서 가져오기 때문에 JPA 내부에서 최적화한다.
하지만 JPQL은 입력한 query string이 SQL에 그대로 변환된다.
Member는 Team을 즉시로딩하므로 반환하는 시점에 Team이 다 조회되어 있어야 한다.
따라서 Member를 모두 가져온 다음, Member와 연관된 Team을 다 가져온다.
(지연로딩이라면 Team은 프록시 객체를 넣을 수 있다.)
Member 객체가 2개이고 각각 다른 Team과 연결되어 있다고 해보자.
Team teamA = new Team();
teamA.setName("teamA");
em.persist(teamA);
Team teamB = new Team();
teamB.setName("teamB");
em.persist(teamB);
Member member1 = new Member();
member1.setUsername("member1");
member1.setTeam(teamA);
em.persist(member1);
Member member2 = new Member();
member2.setUsername("member2");
member2.setTeam(teamB);
em.persist(member2);
em.flush();
em.clear();
//Member m = em.find(Member.class, member1.getId());
List<Member> members =
em.createQuery("select m from Member m", Member.class)
.getResultList();
Member를 JPQL로 조회하면 총 3개 (1+2)의 쿼리가 DB로 날아간다.
Member를 모두 찾는 쿼리를 한 번 던졌다.
그 후 Member가 총 2개이기 때문에 각 Member 당 연관된 Team을 조회하기 위해 쿼리가 두 번 더 나간다.
그러면 Member에 Team 값이 모두 채워진다.
Reference
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의
JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런
www.inflearn.com
'스프링부트 이론 > JPA' 카테고리의 다른 글
[프록시와 연관관계 관리] 실전 예제 (0) | 2023.05.29 |
---|---|
[프록시와 연관관계 관리] 영속성 전이, 고아객체 (0) | 2023.05.28 |
[프록시와 연관관계 관리] 프록시 (0) | 2023.05.28 |
[고급 매핑] @MappedSuperclass (0) | 2023.05.27 |
[고급 매핑] 상속 관계 매핑 (0) | 2023.05.27 |