Computer Science/Spring & Java

[JPA] 즉시 로딩과 지연 로딩

콩순이냉장고 2022. 11. 21. 01:44

프록시 객체는 주로 연관된 엔티티를 지연 로딩할 때 사용

 

즉시 로딩 : 엔티티를 조회할 때 연관된 엔티티도 함께 조회

-설정방법 : @ManyToOne(fetch = FetchType.EAGER) //이미 ManyToOne에선 fetchType은 EAGER이 default

 

지연 로딩 : 연관된 엔티티를 실제 사용할 때 조회

-설정방법 : @ManyToOne(fetch = FetchType.LAZY)

 

@Entity
 public class Member {
 @Id
 @GeneratedValue
 private Long id;
 @Column(name = "USERNAME")
 private String name;
 @ManyToOne(fetch = FetchType.EAGER) //**
 @JoinColumn(name = "TEAM_ID")
 private Team team;
 ..

 

실행코드 :

 Member member = em.find(Member.class, memberId);
 Team team = member.getTeam(); //객체 그래프 탐색

실제 em.find(Member.class, memberId);로 회원을 조회하는 순간 팀도 함께 조회

JPA 구현체는 즉시 로딩을 최적하하기 위해 가능하면 조인쿼리를 사용 

 

SELECT
	M.MEMBER_ID AS MEMBER_ID,
    M.TEAM_ID AS TEAM_ID
    M.USERNAME AS USERNAME,
    T.TEAM_ID AS TEAM_ID,
    T.NAME AS NAME
FROM
	MEMBER M LEFT OUTER JOIN TEAM T
    	ON M.TEAM_ID = T.TEAM_ID
WHERE
	M.MEMBER_ID =?

팀은 조인해서 쿼리 한 번으로 조회

 

NULL 제약조건과 JPA 조인 전략

즉시 로딩 실행 SQL에서 JPA가 INNER JOIN이 아닌 LEFT OUTER JOIN을 사용한다  현재 회원 테이블에 TEAM_ID 외래키는 NULL 값을 허용하기 때문에 팀에 소속되지 않는 회원이 있을 가능성이 있다.

따라서 팀에 소속하지 않은 회원과 팀을 INNER JOIN 하면 팀은 물론이고 회원 데이터도 조회할 수 없다.

 

그러나 OUTER JOIN보단 INNER JOIN이 성능과 최적화에서 더유리, INNER  JOIN을 사용하기 위해선 외래 키에 NOT NULL 제약 조건을 설정하면 값이 있는것을 보장하기때문에 INNER JOIN이 사요됨 JPA에게 이러한 사실을 알려주기위해

@JoinColumn 에 nullable =false을 설정하면됨

 

 

@JoinColumn(nullable =true) : NULL 허용 외부조인 사용

@JoinColumn(nullable =true) : NULL 허용x 내부조인 사용

 

지연로딩(LAZY LOADING)

지연로딩을 사용하기 위해선 @ManyToOne의 fetch 속성을 FetchType.LAZY로 지정

 @Entity
 public class Member {
 @Id
 @GeneratedValue
 private Long id;
 @Column(name = "USERNAME")
 private String name;
 @ManyToOne(fetch = FetchType.LAZY) //**
 @JoinColumn(name = "TEAM_ID")
 private Team team;
 .. 
 }

 

 Member member = em.find(Member.class, memberId);
 Team team = member.getTeam(); //객체 그래프 탐색, 프록시객체
 team.getName();//팀 객체 실제 사용

 

 

 

 

회원과 팀을 지연로딩으로 설정시 em.find(Member.class,memberId)를 호출하면 회원만 조회하고 팀은 조회 하지않음

대신에 team 멤버변수에 프록시 객체를 넣어둠

 

 

프록시객체는 실제 사용될 때까지 데이터 로딩을 미룸 그래서 지연로딩이라 함

 

em.find(Member.class)일때 

SELECT * FROM MEMBER
WEHERE MEMBER_ID =memberId

team.getName() 호출로 프록시 객체가 초기화되면서 실행되는 SQL

 

SELECT * FROM TEAM
WHERE TEAM_ID = 'team1'

프록시와 즉시로딩 주의

• 가급적 지연 로딩만 사용(특히 실무에서)

• 즉시 로딩을 적용하면 예상하지 못한 SQL이 발생

• 즉시 로딩은 JPQL에서 N+1 문제를 일으킨다.

• @ManyToOne, @OneToOne은 기본이 즉시 로딩 -> LAZY로 설정

• @OneToMany, @ManyToMany는 기본이 지연 로딩