Team과 Member가 1:N으로 매핑된다고 가정해보자.
이때 Member를 조회할 때 Team도 함께 조회해야할까?
비즈니스 로직에서 Team을 필요로 하지 않을 때에는 꼭 TEAM도 같이 조회할 필요가 없다.
➡️ JPA는 이런 낭비(성능저하)를 하지 않기 위해지연로딩
과프록시
라는 개념으로 해결한다.
간단히 말해서 가짜 객체를 의미한다.
실제 엔티티 객체 조회
가짜(프록시) 엔티티 객체 조회
DB에 직접 조회해서 값을 가져와 entity를 만들어 내는 과정
(1) Member member = em.getReference(...); ->
프록시 객체 호출
(2) member.getName(); -> 영속성 컨텍스트에초기화
요청
(3) 영속성 컨텍스트는 DB조회해서 실제 엔티티 객체 생성
(4) target에 실제 엔티티 객체를 연결시켜준다
➡️ 즉, target의 getName()을 통해서 멤버의 getName()이 반환됨
(5) 이 후, member의 getName()을 하게되면 기존에 조회된 것이 있으므로 target에서 name을 조회한다.
실제 클래스를 상속
받아서 만들어진다실제 객체의 참조(target)
를 보관한번만 초기화
된다em.getReference()
를 호출해도 실제 엔티티를 반환한다. PersistenceUnitUtil.isLoaded(Object entity)
entity.getClass().getName
출력org.hibernate.Hibernate.initalize(entity);
📌참고
JPA 표준은 강제 초기화 없다
강제 호출 : member.getName()
필요한 시점에 연관된 객체의 데이터를 불러오는 것
➡️ (fetch = FetchType.LAZY)
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String username;
@ManyToOne(fetch = FetchType.LAZY) //패치타입 LAZY : 프록시로 가져옴
@JoinColumn(name ="TEAM_ID")
private Team team;
...
}
- 프록시 객체로 조회한다
➡️지연로딩을 의미- Member만 DB에서 조회하고 team은 프록시로 조회
Team team1 = new Team();
team1.setName(team1);
em.persist(team1);
Member ember1 = new Member();
member1.setUsername("member1");
member1.setTeam(team);
em.persist(member1);
Member m =em.find(Member.class, member.getId());
//team의 속성 조회
System.out.println("m = " + m.getTeam().getName());
- m에 있는 team을 가져올 때 프록시를 가져온다
➡️직접적으로 team을 사용하지 않았을 때
- team내에 속성을 조회하게 되면 이 시점에서 DB에서 쿼리로 조회한다.
➡️team의 필드를 사용할때 실제 쿼리문이 나간다.
데이터를 조회할 때, 연관된 모든 객체의 데이터까지 한 번에 불러오는 것
➡️ (fetch = FetchType.EAGER)
@Entity
public class Member extends BaseEntity{
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String username;
//패치타입 EAGER : 프록시X, 바로 가져옴
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name ="TEAM_ID")
private Team team;
Team team1 = new Team();
team1.setName(team1);
em.persist(team1);
Member ember1 = new Member();
member1.setUsername("member1");
member1.setTeam(team);
em.persist(member1);
Member m =em.find(Member.class, member.getId());
//team의 속성 조회
System.out.println("m = " + m.getTeam().getName());
- team의 속성 조회(m.getTeam().getName())시에 team에 대한 조회 쿼리가 나가는 게 아니라
member를 조회할 때 team도 한꺼번에 같이 조회
한다.
N+1 문제
를 일으킨다.fetchJoin
을 사용한다.@EntityGraph
를 사용한다.batchSize
를 설정한다.@ManyToOne
, @OneToOne
은 기본이 즉시로딩@OneToMaany
, @ManyToMany
는 기본이 지연로딩