지연로딩
Q ) 단순히 member 정보만 사용하는 비즈니스 로직이라고 하면,
연관관계가 걸려있다고 해도, Member를 조회할 때 Team도 함께 join해 조회해야 할까?
⇒ JPA는 지연로딩이라는 옵션을 제공 fetch = FetchType.LAZY
@Entity
public class Member extends BaseEntity{
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;
.....
이렇게 fetch = FetchType.LAZY 를 걸어주면, 프록시로 조회를 함.
Member 클래스만 DB에서 조회를 한다는 의미.
Member member1 = new Member();
member1.setName("member1");
em.persist(member1);
em.flush();
em.clear();
Member m = em.find(Member.class, member1.getId());
tx.commit();
Team team = new Team();
team.setName("teamA");
em.persist(team);
Member member1 = new Member();
member1.setName("member1");
member1.setTeam(team);
em.persist(member1);
em.flush();
em.clear();
Member m = em.find(Member.class, member1.getId());
System.out.println("m = " + m.getTeam().getClass());
Team team = new Team();
team.setName("teamA");
em.persist(team);
Member member1 = new Member();
member1.setName("member1");
member1.setTeam(team);
em.persist(member1);
em.flush();
em.clear();
Member m = em.find(Member.class, member1.getId());
System.out.println("m = " + m.getTeam().getClass());
System.out.println("==============================");
//Team을 가져오려는 순간, 프록시 초기화 & DB에서 값을 가져옴
m.getTeam().getName();
System.out.println("==============================");
========= 사이에 TEAM관련 쿼리를 확인할 수 있음.!!!
지연로딩으로 세팅하면, 연관된 것을 Proxy로 가져옴!!!⭐
즉시로딩
Q ) Member와 Team을 자주 함께 사용하는 경우에는??
⇒ fetch = FetchType.EAGER
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "TEAM_ID")
private Team team;
쿼리가 MEMBER와 TEAM이 한 번에 같이 조인해서 나옴
즉, 프록시가 필요 없음. (둘 다 실제 객체 조회)
But ⇒ 실무에서는 즉시로딩 사용하지 말자! (가급적 지연로딩만 사용)
- 즉시 로딩을 적용하면 예상하지 못한 SQL이 발생
- 즉시 로딩은 JPQL에서 N+1 문제를 일으킨다.
- @ManyToOne, @OneToOne은 기본이 즉시 로딩 -> LAZY로 설정
- @OneToMany, @ManyToMany는 기본이 지연 로딩
//Member m = em.find(Member.class, member1.getId());
//JPQL
List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();
//SQL : select * from Member
//SQL : select * from TEAM where TEAM_ID = xxx
- em.find
- em.find() 메서드를 사용하면 내부적으로 한 번의 쿼리로 데이터를 가져옴. 예를 들어, 특정 Member를 조회할 때 연관된 Team도 함께 조회됨
- JPQL
- JPQL 쿼리를 사용하면, 해당 JPQL이 그대로 SQL로 변환되어 실행
🚨 JPQL에서의 N+1 문제 🚨
- JPQL로 select m from Member m 쿼리를 실행하면, 먼저 Member 엔티티에 대한 쿼리가 실행. (select * from Member)
- 이때, 각 Member에 연관된 Team을 EAGER로 설정했으면
- 각 Member마다 추가로 Team을 조회하는 쿼리가 발생⭐⭐⭐(select * from TEAM where TEAM_ID = xxx)
- Member 10명을 조회할 경우, 첫 번째 쿼리(select * from Member)가 한 번 실행되고,
- 이후 각 Member마다 연관된 Team을 조회하기 위해 10번의 추가 쿼리(select * from TEAM where TEAM_ID = xxx)가 실행됨.
- 총 11번의 쿼리가 실행되는 상황이 발생=⇒ 이게 N+1 문제!!!!!
IF) MEMBER와 TEAM을 같이 조회해야 한다면 JPQL에서 fetch join을 사용하면 됨
List<Member> members = em.createQuery(
"SELECT m FROM Member m JOIN FETCH m.team", Member.class
).getResultList();
반응형