Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- Thymeleaf
- Exception
- kotlin
- Spring Boot
- Android
- AOP
- transaction
- Servlet
- db
- Greedy
- 그리디
- pointcut
- 스프링 핵심 기능
- SpringBoot
- 인프런
- Proxy
- 김영한
- 자바
- java
- 알고리즘
- JPQL
- QueryDSL
- jpa
- springdatajpa
- http
- 스프링
- 스프링 핵심 원리
- spring
- JDBC
- 백준
Archives
- Today
- Total
개발자되기 프로젝트
즉시 로딩과 지연 로딩. 본문
1. 지연로딩
- LAZY FETCH를 사용해서 프록시로 조회.
-
@Entity public class Member extends BaseEntity{ @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "TEAM_ID") private Team team;
-
Member member = new Member(); member.setUserName("hello"); em.persist(member); em.flush(); em.clear(); Member m = em.find(Member.class, member.getId()); tx.commit();
- 로그를 보면 SELECT 쿼리를 통해 member만 읽어온다.
-
Hibernate: select member0_.MEMBER_ID as member_i1_4_0_, member0_.createdBy as createdb2_4_0_, member0_.createdDate as createdd3_4_0_, member0_.lastModifiedBy as lastmodi4_4_0_, member0_.lastModifiedDate as lastmodi5_4_0_, member0_.modifiedBy as modified6_4_0_, member0_.LOCKIER_ID as lockier_8_4_0_, member0_.TEAM_ID as team_id9_4_0_, member0_.USERNAME as username7_4_0_, locker1_.id as id1_3_1_, locker1_.name as name2_3_1_ from Member member0_ left outer join Locker locker1_ on member0_.LOCKIER_ID=locker1_.id where member0_.MEMBER_ID=?
- 이 상태에서 member.getTeam.getClass를 하면?
-
Team team = new Team(); team.setName("teamA"); em.persist(team); Member member = new Member(); member.setUserName("hello"); member.setTeam(team); em.persist(member); em.flush(); em.clear(); Member m = em.find(Member.class, member.getId()); System.out.println("m = " + m.getTeam().getClass()); tx.commit();
- Select 쿼리를 통해 member만 가져오고
- member.getTeam을 통해 가져온 객체는 프록시이다.
-
Hibernate: select member0_.MEMBER_ID as member_i1_4_0_, member0_.createdBy as createdb2_4_0_, member0_.createdDate as createdd3_4_0_, member0_.lastModifiedBy as lastmodi4_4_0_, member0_.lastModifiedDate as lastmodi5_4_0_, member0_.modifiedBy as modified6_4_0_, member0_.LOCKIER_ID as lockier_8_4_0_, member0_.TEAM_ID as team_id9_4_0_, member0_.USERNAME as username7_4_0_, locker1_.id as id1_3_1_, locker1_.name as name2_3_1_ from Member member0_ left outer join Locker locker1_ on member0_.LOCKIER_ID=locker1_.id where member0_.MEMBER_ID=? ============================ m = class hellojpa.Team$HibernateProxy$Asb0JG8r ============================
- 즉 LAZY fetch를 적용하면 Member를 불러올 때 Team은 프록시로 불러온다.
- 이 다음에 team에 있는 name이나 다른 애들을 불러올 때,
- 예를들어 team.getName을 할 때 쿼리가 날라간다.
-
Team team = new Team(); team.setName("teamA"); em.persist(team); Member member = new Member(); member.setUserName("hello"); member.setTeam(team); em.persist(member); em.flush(); em.clear(); Member m = em.find(Member.class, member.getId()); System.out.println("============================"); System.out.println("m = " + m.getTeam().getClass()); System.out.println("============================"); System.out.println("============================"); m.getTeam().getName(); System.out.println("============================"); tx.commit();
-
============================ m = class hellojpa.Team$HibernateProxy$w0kqwL1y ============================ ============================ Hibernate: select team0_.TEAM_ID as team_id1_8_0_, team0_.createdBy as createdb2_8_0_, team0_.createdDate as createdd3_8_0_, team0_.lastModifiedBy as lastmodi4_8_0_, team0_.lastModifiedDate as lastmodi5_8_0_, team0_.modifiedBy as modified6_8_0_, team0_.name as name7_8_0_ from Team team0_ where team0_.TEAM_ID=? ============================
- team.getName()을 호출하는 시점에 Team을 조회하는 쿼리가 나온다.
2. 지연로딩 : LAZY를 사용해서 프록시로 조회.
3. EAGER Fetch
- Member 조회시 항상 Team 조회가 필요한 경우.
- EAGER, 즉시로딩.
- EAGER
@Entity
public class Member extends BaseEntity{
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "TEAM_ID")
private Team team;
- 쿼리는 두 번 날라갈까? 아님 조인할까?
Hibernate:
select
member0_.MEMBER_ID as member_i1_4_0_,
member0_.createdBy as createdb2_4_0_,
member0_.createdDate as createdd3_4_0_,
member0_.lastModifiedBy as lastmodi4_4_0_,
member0_.lastModifiedDate as lastmodi5_4_0_,
member0_.modifiedBy as modified6_4_0_,
member0_.LOCKIER_ID as lockier_8_4_0_,
member0_.TEAM_ID as team_id9_4_0_,
member0_.USERNAME as username7_4_0_,
locker1_.id as id1_3_1_,
locker1_.name as name2_3_1_,
team2_.TEAM_ID as team_id1_8_2_,
team2_.createdBy as createdb2_8_2_,
team2_.createdDate as createdd3_8_2_,
team2_.lastModifiedBy as lastmodi4_8_2_,
team2_.lastModifiedDate as lastmodi5_8_2_,
team2_.modifiedBy as modified6_8_2_,
team2_.name as name7_8_2_
from
Member member0_
left outer join
Locker locker1_
on member0_.LOCKIER_ID=locker1_.id
left outer join
Team team2_
on member0_.TEAM_ID=team2_.TEAM_ID
where
member0_.MEMBER_ID=?
- EAGER 타입일 경우 두 테이블을 조인해서 한 번에 가져온다.
- 즉 즉시로딩 이기 때문에, 프록시를 가져올 필요가 없다.
-
============================ m = class hellojpa.Team ============================
- JPA 구현체는 가능하면 조인을 사용해서 SQL 한번에 함께 조회.
3. 프록시와 즉시로딩 주의
- 가급적 지연 로딩만 사용(특히 실무에서..ㅜ)
- 즉시 로딩을 적용하면 예상치 못한 SQL 이 발생
- 즉시 로딩은 JPQL에서 N+1 문제를 일으킨다.
- EAGER로 세팅하고 JPQL을 사용하면 N+1이슈 발생 가능
-
List<Member> memberList = em.createQuery("select m from Member m", Member.class).getResultList();
-
Hibernate: /* select m from Member m */ select member0_.MEMBER_ID as member_i1_4_, member0_.createdBy as createdb2_4_, member0_.createdDate as createdd3_4_, member0_.lastModifiedBy as lastmodi4_4_, member0_.lastModifiedDate as lastmodi5_4_, member0_.modifiedBy as modified6_4_, member0_.LOCKIER_ID as lockier_8_4_, member0_.TEAM_ID as team_id9_4_, member0_.USERNAME as username7_4_ from Member member0_ Hibernate: select team0_.TEAM_ID as team_id1_8_0_, team0_.createdBy as createdb2_8_0_, team0_.createdDate as createdd3_8_0_, team0_.lastModifiedBy as lastmodi4_8_0_, team0_.lastModifiedDate as lastmodi5_8_0_, team0_.modifiedBy as modified6_8_0_, team0_.name as name7_8_0_ from Team team0_ where team0_.TEAM_ID=?
- em.find()는 PK를 딱 지정해서 조회가 가능.
- 하지만 JPQL은 쿼리문이 SQL로 번역됨. 따라서 위와 같은 경우는 Member만 가지고옴.
- 그러면 Member를 가지고왔더니 EAGER네???? TEAM도 가져오자 이렇게됨. ㄷㄷ
- 해결방법
- 일단 모두 LAZY 타입
- fetch Join 동적으로 원하는 대로 가져옴
List<Member> memberList = em.createQuery("select m from Member m join fetch m.team", Member.class) .getResultList();
-
Hibernate: /* select m from Member m join fetch m.team */ select member0_.MEMBER_ID as member_i1_4_0_, team1_.TEAM_ID as team_id1_8_1_, member0_.createdBy as createdb2_4_0_, member0_.createdDate as createdd3_4_0_, member0_.lastModifiedBy as lastmodi4_4_0_, member0_.lastModifiedDate as lastmodi5_4_0_, member0_.modifiedBy as modified6_4_0_, member0_.LOCKIER_ID as lockier_8_4_0_, member0_.TEAM_ID as team_id9_4_0_, member0_.USERNAME as username7_4_0_, team1_.createdBy as createdb2_8_1_, team1_.createdDate as createdd3_8_1_, team1_.lastModifiedBy as lastmodi4_8_1_, team1_.lastModifiedDate as lastmodi5_8_1_, team1_.modifiedBy as modified6_8_1_, team1_.name as name7_8_1_ from Member member0_ inner join Team team1_ on member0_.TEAM_ID=team1_.TEAM_ID
- @ManyToOne, @OneToOne은 기본이 EAGER 타입임.
--> LAZY로 변경해서 사용하자. - @OneToMany, @ManyToMany는 기본이 LAZY
4. 지연 로딩 활용
- 근데 실무세어는 무조건 lazy ㅋㅋㅋㅋ
- Member와 Team이 자주 함께 사용된다? -> 즉시 로딩
- Member와 Team은 가끔 사용 -> 지연 로딩
- Order와 Product는 자주 함께 사용 -> 즉시 로딩
5. 지연로딩 활용 - 실무
- 모든 연관관계에 지연로딩 사용!
- 실무에서 즉시 로딩 쓰지마
- JPQL에서 fetch join이나, entity graph 기능을 사용해
- 즉시 로딩은 상상치 못한 쿼리가 나간다. ㄷㄷㄷ
5. GitHub : 210812 LAZY FETCH
'인프런 > [인프런] 자바ORM 표준 JPA 프로그래밍' 카테고리의 다른 글
[예제] 연관관계 관리 (0) | 2021.08.12 |
---|---|
영속성 전이(CASCADE),고아 객체 (0) | 2021.08.12 |
프록시 (0) | 2021.08.12 |
[예제] 상속관계 매핑 (0) | 2021.08.11 |
@MappedSuperclass (0) | 2021.08.11 |
Comments